#!/bin/sh
# @(#) MQMBID sn=p943-001-250813.2 su=4ccc58ef4ad72edebab850c9cef04037b8b261ad pn=cmd/install/unix/mqconfig.sh
#
#   <copyright
#   notice="lm-source-program"
#   pids="5724-H72"
#   years="2011,2025"
#   crc="1956258177" >
#   Licensed Materials - Property of IBM
#
#   5724-H72
#
#   (C) Copyright IBM Corp. 2011, 2025 All Rights Reserved.
#
#   US Government Users Restricted Rights - Use, duplication or
#   disclosure restricted by GSA ADP Schedule Contract with
#   IBM Corp.
#   </copyright>
#
### NAME:      mqconfig
###
###
### SYNOPSIS:
###
###  mqconfig -?
###  mqconfig -v Version
###
###         -?: Display help for the mqconfig script
###         -v: IBM MQ version: 9.4, 9.3, 9.2, etc
###
### DESCRIPTION:
###
### This script validates kernel parameters and other resource limits on
### AIX, Linux and Mac systems against the recommendations in
### the IBM MQ documentation:
###
###   https://www.ibm.com/docs/en/ibm-mq
###
###
### CAVEATS/WARNINGS:
###
### Successful validation by this script only confirms your system meets
### the default recommendations.  If your system is heavily stressed, if
### you are running many queue managers at once, or if your system hosts
### other programs like databases which make heavy resource demands, you
### may need to increase your settings beyond the default values.
###
### Where possible this script issues a warning for parameters which are
### lower than recommended but sufficient for a small system.  Generally
### values at 75% or better of the recommended limit cause a warning and
### not a failure, but be aware IBM MQ can fail if it exhausts the
### resource on your system.  For further guidance see:
###
###   https://www.ibm.com/support/pages/node/707975
###
### Certain limits are defined on a per-user basis, e.g. with the ulimit
### shell command.  These limits should be configured for the 'mqm' user
### so that queue managers started by mqm (directly, or using sudo) will
### run with the properly limits.  However, IBM MQ commands can be
### run by any user in the 'mqm' group, and sometimes by anybody.  Since
### the mqconfig script does not know what users will start IBM MQ
### processes on your system, it shows limits for the current user only.
### You can run mqconfig as mqm, as root, and again as any other user on
### the system to verify that those logins have the proper limits to run
### IBM MQ commands.
###
###
### RETURNED VALUES:
###
###   0  - Passed all tests
###   1  - Passed with warnings
###   2  - Failed some tests
###
###
### EXAMPLES:
###
### 1. To check your settings for IBM MQ 9.3:
###
###      mqconfig -v 9.3
###
###
### 2. To get help with the mqconfig script:
###
###      mqconfig -?
###




### The Message function formats messages with caller-supplied values to
### fit an 80-character line and writes them to stdout.  In some
### shells the getopt builtin does not allow us to identify which option
### flag caused the error, so we suppress those messages.

  Message() {
    MSGID=$1; shift

    { case "${MSGID:=9999}" in
        1000) if [ -z "$1" ]; then
                return 0
              fi

              cat <<- :END
		Option -$1 requires an argument.
		:END
              ;;

        1001) if [ -z "$1" ]; then
                return 0
              fi

              cat <<- :END
		Option -$1 is not valid.
		:END
              ;;

        1002) cat <<- :END
		You must provide a IBM MQ version.
		:END
              ;;

        1003) cat <<- :END
		IBM MQ V$1 is not supported.
		:END
              ;;

        1004) cat <<- :END
		Unexpected parameters: $@
		:END
              ;;

        1005) cat <<- :END
		Analyzing $1 settings for IBM MQ V$2
		:END
              ;;

        1006) cat <<- :END
		This script does not support $@.
		:END
              ;;

        1009) cat <<- :END
		IBM MQ V$1 does not exist.
		:END
              ;;

        1010) cat <<- :END
		IBM MQ V$1 does not support $2 $3.
		:END
              ;;

        1011) cat <<- :END
		IBM MQ V$1 is no longer supported by this script.
		Please download the mqconfig-old script from the IBM site in
		order to analyze a WebSphere MQ V6.0 or V5.3 system.
		:END
              ;;

        1012) cat <<- :END
		WebSphere MQ V7.0 requires AIX 5.3 Technology Level 4 or later,
		and at Technology Level 5 it requires Service Pack 2 or later,
		and at Technology Level 7 it requires Service Pack 1 or later.
		Please refer to the Systems Requirement page on the web for
		further details and current status.
		:END
              ;;

        1013) cat <<- :END
		IBM MQ V$1 requires AIX 6.1 Technology Level 5 or later.
		This AIX 6.1 installation is at Technology Level $2.
		:END
              ;;

        1016) cat <<- :END
		The $1 program was not found on this system.  Please install
		$1 and try running mqconfig again.
		:END
              ;;

        1017) cat <<- :END
		IBM MQ V$1 requires AIX 6.1 Technology Level 8 or later.
		This AIX 6.1 installation is at Technology Level $2.
		:END
              ;;
        1018) cat <<- :END
		IBM MQ V$1 requires AIX $2 Technology Level $3 or later.
		This AIX $2 installation is at Technology Level $4.
		:END
              ;;

        1019) cat <<- :END
		Any values listed in the "Current User Limits" section are resource
		limits for the user who ran mqconfig.

		If the user account that is used to invoke this script ($1) is not the
		same as the user account that is used to start the queue manager, then
		the assessed values will not be accurate.

		If you normally start your queue managers as the mqm user, you should
		switch to mqm and run mqconfig there.

		If other members of the mqm group also start queue managers, all those
		members should run mqconfig, to ensure that their limits are suitable
		for IBM MQ.
		:END
              ;;

        1020) cat <<- :END
		A PASS score means your system meets the minimum IBM recommendations
		but busy systems might need higher limits to run production workloads.

		For performance-critical environments, further performance testing
		should always be conducted using workloads that are representative of
		the real volume.
		:END
              ;;
           *) cat <<- :END
		Cannot print message: $MSGID $@
		:END
              ;;
      esac } | env LANG=C tr -d '\t' | fmt -68 | {
        read LINE && printf "mqconfig: $LINE\n"
        while read LINE; do
          printf "          $LINE\n"
        done
    }

    return 0
  }


### The Error function prints a message to stderr, and optionally prints
### the message to stdout when stdout is redirected to a file.  Provided
### the shell supports the '-t' test, this ensures that IBM will receive
### a file which includes any errors reported to the user.

  Error() {
    Message "$@" >&2

    if [ -t 2 -a ! -t 1 ]; then
      Message "$@"
    fi

    return 0
  }


### Print the script syntax for this operating system to stderr.

  PrintSyntax() {
    printf "\n%s\n" "syntax:   mqconfig -?" >&2

    case "$OPSYS" in
             *) printf "%s\n" "          mqconfig -v Version" >&2
                ;;
    esac

    printf "\n          Version: 9.4, 9.3, 9.2, 9.1, 9.0, 8.0, 7.5, 7.1 or 7.0\n" >&2
    return 0
  }


### The PrintHelp function displays information about how mqconfig works
### and addresses frequently asked questions from users.  This method is
### invoked when a user runs 'mqconfig -?'.

  PrintHelp() {
    cat <<- :END
	Using mqconfig
	`PrintSyntax 2>&1 | sed 's/^/  /'`

	  The mqconfig script analyzes your AIX, Linux or Mac system to make
	  sure its kernel paramaters and other settings match the values recommended by
	  IBM in the IBM MQ documentation.  For each parameter, mqconfig displays
	  the current value, the current resource usage where possible, the recommended
	  setting from IBM, and a PASS/WARN/FAIL grade.

	  The grade assigned to each setting is based on its proximity to the IBM value
	  and your resource usage.  For example, if the IBM recommended value is 10000,
	  a setting of 2000 will fail, 8000 will give a warning, and 10000 or more will
	  pass.  However, mqconfig will issue a warning if your system's resource usage
	  is high (typically 75% or more) even if your system meets the IBM recommended
	  value.  If your system is about to exhaust a resource (95% or more), mqconfig
	  will report that as a failure.  In such cases, you may need to choose a value
	  which exceeds the default IBM recommendation.


	User Limits

	  Certain resource limits affecting IBM MQ are user specific, and are set
	  using the ulimit shell command.  Even though IBM MQ effectively runs as
	  the mqm user, the resource limits applicable to the queue manager derive from
	  the shell of the user who starts it.  The mqconfig script displays the limits
	  for the current user only, so you should run mqconfig as mqm, or root, or any
	  other user who will start queue managers to ensure their resource limits will
	  not prevent the queue manager from running properly.


	Shell Options

	  The IBM MQ documentation warns of potential performance problems caused
	  by starting IBM MQ commands in a shell which runs background tasks at a
	  lower priority.  The mqconfig script will try to determine whether your shell
	  includes an option to reduce the priority of background processes, and if so,
	  it will show the current default.  If mqconfig identifies a problem with your
	  shell options, you can change them in your profile.  For example, 'ksh' users
	  can add the line 'set +o bgnice' to their profile to avoid this issue.


	Planning for Heavy Workloads

	  IBM MQ resource usage depends on how heavily you are using IBM MQ
	  including the number of channels, the number of clients, the number of queues
	  and messages on them.  A single busy queue manager may use far more resources
	  than ten test queue managers with light usage.  If you are using IBM MQ
	  very heavily, you should run mqconfig while processing your workload in order
	  to ensure that IBM MQ is not about to exhaust any resources.  It may be
	  necessary to increase your resource limits beyond the defaults recommended by
	  IBM in order to process heavy workloads.

	:END

    return 0
  }


### The Sum function is a helper to sum up columns of numbers, while the
### Trim function removes leading and trailing space from values (useful
### when dealing with the output from 'wc -l').

  Sum() {
    SUM=0

    while read NUM ; do
      SUM=`expr $SUM + $NUM`
    done

    printf "$SUM"
  }


  Trim() {
    sed -e 's/^[ 	]*//' -e 's/[ 	]*$//'
  }


### Determine the current usage of those kernel parameters for which the
### operating system provides a method.  These results will be reflected
### in the output as a percentage of resources consumed.  For the number
### of processes per user we look at the number of processes running for
### the current user and any others with active queue manager processes,
### except for root, and print the highest value. Although IBM MQ
### processes have an effective user of mqm, they count against the real
### user for accounting purposes.

  GetResourceUsage() {
    PARAM=$1

    case $OPSYS:$PARAM in
            Linux:file-max) GetLinuxValue fs.file-nr sys/fs/file-nr | awk '{print $1}'
                            ;;
             Linux:pid_max) ps -eL 2>/dev/null | wc -l | Trim
                            ;;

         Linux:threads-max) ps -eL 2>/dev/null | wc -l | Trim
                            ;;

      *:maxup*|Linux:nproc) { for USER in `id -un 2>/dev/null` `env UNIX95=1 ps -e -o user= -o comm= |
                               grep -E '[a]mqzxma0|[a]mqzmgr0' | awk '{print $1}' | sort -u`; do

                                if [ $USER != root ]; then
                                  if [ "$OPSYS" = "Linux" ]; then
                                    env UNIX95=1 ps -o pid= -L -u $USER | wc -l
                                  else
                                    env UNIX95=1 ps -o pid= -u $USER | wc -l
                                  fi
                                fi
                              done

                              printf "0\n"
                            } | sort -rn | head -n 1 | Trim
                            ;;

              Linux:semmni) ipcs -s 2>/dev/null | grep '^0x' | wc -l | Trim
                            ;;

         *:semmni|*:SEMMNI) ipcs -s 2>/dev/null | grep '^s' | wc -l | Trim
                            ;;

              Linux:semmns) ipcs -s 2>/dev/null | grep '^0x' | awk '{print $5}' | Sum
                            ;;

         *:semmns|*:SEMMNS) ipcs -sa 2>/dev/null | grep '^s' | sed 's/^./& /' |
                            awk '{print $9}' | Sum
                            ;;

              Linux:shmall) ipcs -u 2>/dev/null | grep 'pages allocated' | awk '{print $3}'
                            ;;

              Linux:shmmni) ipcs -m 2>/dev/null | grep '^0x' | wc -l | Trim
                            ;;

         *:shmmni|*:SHMMNI) ipcs -m 2>/dev/null | grep '^m' | wc -l | Trim
                            ;;
    esac
  }


### The DisplayLimit function compares the current value of an operating
### system parameter to the limit recommended by IBM, and where possible
### shows the current resource usage.  In addition to the IBM limit this
### function accepts an optional warning limit, expressed as an absolute
### value or as a percentage of the IBM limit.  Values which do not meet
### the IBM limit but are reasonably close will receive a warning.  When
### the warning limit is greater than the IBM limit, the parameter needs
### a low value; Simply negating all the values allows the same logic to
### calculate the grade.  We use bc to perform all comparisons and math,
### except for unknown or unlimited values, since it is easy to overflow
### the arithmetic precision of the shell.  After printing the parameter
### name, value, current usage (where possible), and the IBM recommended
### limit, this function assigns a grade based on these basic rules:
###
###  PASS: Meets or exceeds the IBM value and usage is less than 75%
###  WARN: Limit approaches the IBM value or usage is between 75-95%
###  FAIL: Limit is less than the IBM value or its usage exceeds 95%

  DisplayLimit() {
    PARAM=$1
    UNITS=$2
    VALUE=$3
    LIMIT=$4
    ALERT=$5

    CURRENT=`GetResourceUsage $PARAM`
    if [ -z "$LIMIT" ]; then
      return
    elif [ -z "$VALUE" ]; then
      return
    elif [ "$UNITS" = "$LITERAL" ]; then
      printf "  %-19s %-34s %-17s %b\n" "$PARAM" "$VALUE" "IBM:$LIMIT" "$ALERT"
      return
    elif [ "$LIMIT" = "$AUTO" ]; then
      printf "  %-19s %-34s %-17s %b\n" "$PARAM" "${CURRENT:=$UNKNOWN} $UNITS" "[Auto Tuned]" $PASS
      return
    elif [ -n "$CURRENT" -a "$VALUE" != "$UNKNOWN" -a "$VALUE" != "$UNLIMITED" -a "$VALUE" != "0" ]; then
      PERCENT=`printf "$CURRENT * 100 / $VALUE\n" | bc 2>/dev/null`
      printf "  %-19s %-26s %-7s " "$PARAM" "$CURRENT of $VALUE $UNITS" "(${PERCENT}%)"
    else
      PERCENT=0
      printf "  %-19s %-34s " "$PARAM" "$VALUE $UNITS"
    fi

    case ${ALERT:=$LIMIT} in
      *%) ALERT=`printf "%s" "$ALERT" | env LANG=C tr -d %`
          ALERT=`printf "$LIMIT * $ALERT / 100\n" | env LANG=C tr -d % | bc 2>/dev/null`
          ;;
    esac

    if [ "$LIMIT" != "$UNLIMITED" -a -n "`printf \"if ($ALERT > $LIMIT) 1\n\" | bc 2>/dev/null`" ]; then
      if [ $LIMIT -eq 1 ]; then
        printf "%-17s " "IBM=$LIMIT"
      else
        printf "%-17s " "IBM<=$LIMIT"
      fi

      LIMIT=-$LIMIT; ALERT=-$ALERT; VALUE=-$VALUE
    else
      printf "%-17s " "IBM>=$LIMIT"
    fi

    if [ "$VALUE" = "$UNLIMITED" ]; then
      printf "%b\n" $PASS
    elif [ "$VALUE" = "$UNKNOWN" -o "$LIMIT" = "$UNLIMITED" ]; then
      WARNINGS=`expr $WARNINGS + 1`
      printf "%b\n" $WARN
    elif [ -n "`printf \"if ($VALUE < $ALERT) 1\n\" | bc 2>/dev/null`" -o $PERCENT -gt 95 ]; then
      FAILURES=`expr $FAILURES + 1`
      printf "%b\n" $FAIL
    elif [ -n "`printf \"if ($VALUE >= $LIMIT) 1\n\" | bc 2>/dev/null`" -a $PERCENT -lt 75 ]; then
      printf "%b\n" $PASS
    else
      WARNINGS=`expr $WARNINGS + 1`
      printf "%b\n" $WARN
    fi

    return
  }


### The IBM MQ documentation notes that shells with some notion of
### bgnice (reducing the priority of background processes) can introduce
### performance problems.  When a user starts commmands like runmqlsr in
### the background ('runmqlsr -m QM -t tcp -p 1414 1>/dev/null 2>&1 &'),
### those commands may hold IBM MQ locks longer than usual as they
### are runnning at reduced priority.  It is impossible to test the user
### shell since mqconfig is running in its own shell (/bin/sh).  However
### we can determine which shell is the parent to mqconfig and use it to
### determine the default value of bgnice or similar settings.  The test
### is not a perfect one, so any failure will at most provoke a warning.
### Shells with no notion of bgnice will not generate any output at all.
### From my testing, Bourne and bash shells have no bgnice, while shells
### based on Korn (ksh, pdksh) do.  The zsh shell has a nobgnice option,
### which is the exact inverse of bgnice.  When this function detects an
### option like bgnice, it calls DisplayLimit passing literal values for
### the current and IBM values as opposed to numeric values, and a score
### for the current setting.

  CheckShellDefaultOptions() {
    PSHID=`env UNIX95=1 ps -o ppid= -p $$ 2>/dev/null`
    PSHIDCOMM=`env UNIX95=1 ps -o comm= -p $PSHID 2>/dev/null | sed 's/^-*//'`
    BPSHIDCOMM=`basename $PSHIDCOMM`
    PPSHID=`env UNIX95=1 ps -o ppid= -p $PSHID 2>/dev/null`
    PPSHIDCOMM=`env UNIX95=1 ps -o comm= -p $PPSHID 2>/dev/null | sed 's/^-*//'`
    PSHELL=
    OLDIFS="$IFS"
    IFS='
'

    SHELLS=`sed '/^#/d' /etc/shells 2>/dev/null`
    for shellname in $SHELLS ash bash csh dash jsh ksh ksh93 pfcsh pfksh pfsh psh qsh rbash sh tcsh tsh zsh; do
      if [ "`basename $shellname`" = "$BPSHIDCOMM" ]; then
        PSHELL=$PSHIDCOMM
        break
      fi
    done
    if [ -z "$PSHELL" ]; then
      PSHELL=$PPSHIDCOMM
    fi
    if [ -n "$PSHELL" ]; then
      for SETTING in `$PSHELL -c "set -o" 2>/dev/null | grep bgnice`; do
        case $SETTING in
          nobgnice*off) printf "\nShell Default Options (%s)\n" "`id -un 2>/dev/null`"
                        DisplayLimit "$PSHELL" $LITERAL "nobgnice:off" on $WARN
                        ;;

           nobgnice*on) printf "\nShell Default Options (%s)\n" "`id -un 2>/dev/null`"
                        DisplayLimit "$PSHELL" $LITERAL "nobgnice:on" on $PASS
                        ;;

            bgnice*off) printf "\nShell Default Options (%s)\n" "`id -un 2>/dev/null`"
                        DisplayLimit "$PSHELL" $LITERAL "bgnice:off" off $PASS
                        ;;

             bgnice*on) printf "\nShell Default Options (%s)\n" "`id -un 2>/dev/null`"
                        DisplayLimit "$PSHELL" $LITERAL "bgnice:on" off $WARN
                        ;;
                     *) printf "\nShell Default Options (%s)\n" "`id -un 2>/dev/null`"
                        DisplayLimit "$PSHELL" $LITERAL "No settings needed in this shell" "N/A" $PASS
                        ;;
        esac
      done
    fi

    IFS="$OLDIFS"
  }


### AIX is special in that the kernel has no parameters for System V IPC
### resources; Instead, AIX supports such large values that IBM MQ
### cannot exhaust them, even with databases and other IPC users running
### on the same system.  This function prints the current usage of these
### parameters before checking other settings on the system.  Given that
### there are operating system HIPER APARs which affect IBM MQ, we
### also print their installation status on affected levels of AIX.  The
### instfix command may lack the data to accurately report the status of
### the APARs, so instead we look at the affected LPP versions:
###
###   AIX V7.1 TL0: bos.mp64 7.1.0.0 - 7.1.0.1 are vulnerable
###                 bos.mp64 7.1.0.2 and later include IZ84576
###            TL1: bos.mp64 7.1.1.0 and later are not vulnerable
###
###   AIX V6.1 TL6: bos.mp64 6.1.6.0 - 6.1.6.1 are vulnerable
###                 bos.mp64 6.1.6.2 - 6.1.6.14 include IZ84729
###                 bos.mp64 6.1.6.15 and later include IZ85204
###            TL7: bos.mp64 6.1.7.0 and later are not vulerable
###
### AIX customers often report data conversion problems when they do not
### have the necessary AIX Unicode conversion LPPs installed.  There are
### six conversion LPPs right now, of which three are always checked and
### the remaining ones may be checked based on the current locale.

  AnalyzeAIX() {
    IBM_MAXUPROC=1024
    IBM_NOFILES_HARD=10240
    IBM_NOFILES_SOFT=10240
    IBM_DATA_SOFT=$UNLIMITED
    IBM_STACK_SOFT=$UNLIMITED

    CUR_MAXUPROC=`lsattr -El sys0 -a maxuproc 2>/dev/null | awk '{print $2}'`
    CUR_NOFILES_HARD=`ulimit -Hn 2>/dev/null`
    CUR_NOFILES_SOFT=`ulimit -Sn 2>/dev/null`
    CUR_DATA_SOFT=`ulimit -Sd 2>/dev/null`
    CUR_STACK_SOFT=`ulimit -Ss 2>/dev/null`

    printf "\nOperating System Patches\n"
    ABSTRACT="Applications using user trace hooks fail when trace is enabled"

    case "`lslpp -qcL bos.mp64 2>/dev/null | awk -F: '{print $3}'`" in
       7.1.0.[0-1]) printf "\nOperating System HIPER APARs\n"
                    printf "  %7s: %-63s %b\n" IZ84576 "$ABSTRACT" $FAIL
                    ;;

           7.1.0.*) printf "\nOperating System HIPER APARs\n"
                    printf "  %7s: %-63s %b\n" IZ84576 "$ABSTRACT" $PASS
                    ;;

       6.1.6.[0-1]) printf "\nOperating System HIPER APARs\n"
                    printf "  %7s: %-63s %b\n" IZ84729 "$ABSTRACT" $FAIL
                    ;;

       6.1.6.[2-9]) printf "\nOperating System HIPER APARs\n"
                    printf "  %7s: %-63s %b\n" IZ84729 "$ABSTRACT" $PASS
                    ;;

      6.1.6.1[0-4]) printf "\nOperating System HIPER APARs\n"
                    printf "  %7s: %-63s %b\n" IZ84729 "$ABSTRACT" $PASS
                    ;;

           6.1.6.*) printf "\nOperating System HIPER APARs\n"
                    printf "  %7s: %-63s %b\n" IZ85204 "$ABSTRACT" $PASS
                    ;;

                 *) printf "  %-72s %b\n" "$ABSTRACT" $PASS
                    ;;
    esac

    printf "\nSystem V Semaphores\n"
    DisplayLimit semmni                sets        0                       "$AUTO"
    DisplayLimit semmns                semaphores  0                       "$AUTO"

    printf "\nSystem V Shared Memory\n"
    DisplayLimit shmmni                sets        0                       "$AUTO"

    printf "\nSystem Settings\n"
    DisplayLimit maxuproc              processes  "$CUR_MAXUPROC"          "$IBM_MAXUPROC"       50%

    printf "\nUnicode Filesets used by IBM MQ Data Conversion\n"
    UCSLPPS="bos.iconv.ucs.com bos.iconv.ucs.ebcdic bos.iconv.ucs.pc"

    case "$LANG" in
            ZH_CN*|Zh_CN*) UCSLPPS="$UCSLPPS bos.iconv.ucs.ZH_CN bos.iconv.ucs.Zh_CN"
                           ;;

      Et_EE*|Lt_LT*|Lv_LV) UCSLPPS="bos.iconv.ucs.baltic $UCSLPPS"
                           ;;
    esac

    for LPPNAME in $UCSLPPS; do
      LPPSTAT=$PASS; lslpp -L $LPPNAME 1>/dev/null 2>&1 || LPPSTAT=$WARN

      case $LPPNAME in
        bos.iconv.ucs.baltic) LPPDESC="Unicode Converters for Baltic Countries"
                              ;;

           bos.iconv.ucs.com) LPPDESC="Unicode Converters for AIX Code Sets/Fonts"
                              ;;

        bos.iconv.ucs.ebcdic) LPPDESC="Unicode Converters for EBCDIC Code Sets"
                              ;;

            bos.iconv.ucs.pc) LPPDESC="Unicode Converters for Additional PC Code Sets"
                              ;;

         bos.iconv.ucs.ZH_CN) LPPDESC="Unicode Converters for Simplified Chinese (UTF)"
                              ;;

         bos.iconv.ucs.Zh_CN) LPPDESC="Unicode Converters for Simplified Chinese (GBK)"
                              ;;
      esac

      printf "  %-20.20s  %-49.49s  %b\n" $LPPNAME "$LPPDESC" $LPPSTAT
    done

    printf "\nCurrent User Limits (%s)\n" "${currentusr}"
    DisplayLimit "nofiles      (-Hn)"  files       "$CUR_NOFILES_HARD"     "$IBM_NOFILES_HARD"   75%
    DisplayLimit "nofiles      (-Sn)"  files       "$CUR_NOFILES_SOFT"     "$IBM_NOFILES_SOFT"   75%
    DisplayLimit "data         (-Sd)"  kbytes      "$CUR_DATA_SOFT"        "$IBM_DATA_SOFT"      75%
    DisplayLimit "stack        (-Ss)"  kbytes      "$CUR_STACK_SOFT"       "$IBM_STACK_SOFT"     75%

    CheckShellDefaultOptions

    printf "\n"; Message 1019 "${currentusr}"
    printf "\n"; Message 1020
  }



### Most Linux systems today provide the sysctl program for querying the
### value of kernel parameters, but if that is not available they may be
### read from the proc filesystem.  The value string may contain several
### fields, so it is printed as a string for the caller to dissect.

  GetLinuxValue() {
    PARAM=$1
    PPATH=$2
    VALUE=

    if [ -x /sbin/sysctl ]; then
      VALUE=`/sbin/sysctl -n $PARAM 2>/dev/null`
    fi

    if [ -z "$VALUE" -a -n "$PROCPATH" ]; then
      PROC=`mount -t proc 2>/dev/null | awk '{print $3}'`
      if [ -n "$PROC" -a -r "$PROC/$PROCPATH" ]; then
        VALUE=`cat "$PROC/$PROCPATH"`
      fi
    fi

    printf "$VALUE"
  }


### Analyze the Linux kernel parameter settings based on the values from
### the IBM MQ Information Center.  The documentation does list an
### msgmni value, but that is an error and we do not query msgmni here.

  AnalyzeLinux() {
    case $MQVER in
      7.0|7.1|7.5)
                   IBM_SEMMSL=500
                   IBM_SEMMNS=256000
                   IBM_SEMOPM=250
                   IBM_SEMMNI=1024
                   IBM_SHMMNI=4096
                   IBM_SHMALL=2097152
                   IBM_SHMMAX=268435456
                   IBM_SHMMAX_MIN=33554432
                   IBM_KEEPALIVE=300
                   IBM_KEEPALIVE_MAX=600
                   IBM_PIDMAX=32768
                   IBM_THREADSMAX=32768
                   IBM_FILEMAX=524288
                   IBM_NOFILE_HARD=10240
                   IBM_NOFILE_SOFT=10240
                   IBM_NPROC_HARD=4096
                   IBM_NPROC_SOFT=4096
                   ;;
                *)
                   IBM_SEMMSL=32
                   IBM_SEMMNS=4096
                   IBM_SEMOPM=32
                   IBM_SEMMNI=128
                   IBM_SHMMNI=4096
                   IBM_SHMALL=2097152
                   IBM_SHMMAX=268435456
                   IBM_SHMMAX_MIN=73834496
                   IBM_KEEPALIVE=
                   IBM_KEEPALIVE_MAX=
                   IBM_PIDMAX=32768
                   IBM_THREADSMAX=32768
                   IBM_FILEMAX=524288
                   IBM_NOFILE_HARD=10240
                   IBM_NOFILE_SOFT=10240
                   IBM_NPROC_HARD=4096
                   IBM_NPROC_SOFT=4096
                   ;;
    esac

    CUR_SHMMNI=`GetLinuxValue kernel.shmmni sys/kernel/shmmni`
    CUR_SHMALL=`GetLinuxValue kernel.shmall sys/kernel/shmall`
    CUR_SHMMAX=`GetLinuxValue kernel.shmmax sys/kernel/shmmax`
    CUR_SEM=`GetLinuxValue kernel.sem sys/kernel/sem`
    CUR_SEMMSL=`printf "%s" "$CUR_SEM" | awk '{print $1}'`
    CUR_SEMMNS=`printf "%s" "$CUR_SEM" | awk '{print $2}'`
    CUR_SEMOPM=`printf "%s" "$CUR_SEM" | awk '{print $3}'`
    CUR_SEMMNI=`printf "%s" "$CUR_SEM" | awk '{print $4}'`
    CUR_FILEMAX=`GetLinuxValue fs.file-max sys/fs/file-max`
    CUR_PIDMAX=`GetLinuxValue kernel.pid_max sys/kernel/pid_max`
    CUR_THREADSMAX=`GetLinuxValue kernel.threads-max sys/kernel/threads-max`
    if [ -n "$IBM_KEEPALIVE" ]; then
      CUR_KEEPALIVE=`GetLinuxValue net.ipv4.tcp_keepalive_time`
    fi
    CUR_NOFILE_HARD=`ulimit -Hn 2>/dev/null`
    CUR_NOFILE_SOFT=`ulimit -Sn 2>/dev/null`
    CUR_NPROC_HARD=`ulimit -Hu 2>/dev/null`
    CUR_NPROC_SOFT=`ulimit -Su 2>/dev/null`

    printf "\nSystem V Semaphores\n"
    DisplayLimit "semmsl     (sem:1)"  semaphores  "$CUR_SEMMSL"       "$IBM_SEMMSL"
    DisplayLimit "semmns     (sem:2)"  semaphores  "$CUR_SEMMNS"       "$IBM_SEMMNS"       75%
    DisplayLimit "semopm     (sem:3)"  operations  "$CUR_SEMOPM"       "$IBM_SEMOPM"
    DisplayLimit "semmni     (sem:4)"  sets        "$CUR_SEMMNI"       "$IBM_SEMMNI"       75%

    printf "\nSystem V Shared Memory\n"
    DisplayLimit shmmax                bytes       "$CUR_SHMMAX"       "$IBM_SHMMAX"       "$IBM_SHMMAX_MIN"
    DisplayLimit shmmni                sets        "$CUR_SHMMNI"       "$IBM_SHMMNI"       75%
    DisplayLimit shmall                pages       "$CUR_SHMALL"       "$IBM_SHMALL"       50%

    printf "\nSystem Settings\n"
    DisplayLimit file-max              files       "$CUR_FILEMAX"      "$IBM_FILEMAX"      75%
    DisplayLimit pid_max               processids  "$CUR_PIDMAX"       "$IBM_PIDMAX"       75%
    DisplayLimit threads-max           threads     "$CUR_THREADSMAX"   "$IBM_THREADSMAX"   75%
    if [ -n "$IBM_KEEPALIVE" ]; then
      DisplayLimit tcp_keepalive_time    seconds   "$CUR_KEEPALIVE"    "$IBM_KEEPALIVE"    "$IBM_KEEPALIVE_MAX"
    fi

    printf "\nCurrent User Limits (%s)\n" "${currentusr}"
    DisplayLimit "nofile       (-Hn)"  files       "$CUR_NOFILE_HARD"  "$IBM_NOFILE_HARD"  75%
    DisplayLimit "nofile       (-Sn)"  files       "$CUR_NOFILE_SOFT"  "$IBM_NOFILE_SOFT"  75%
    DisplayLimit "nproc        (-Hu)"  processes   "$CUR_NPROC_HARD"   "$IBM_NPROC_HARD"   75%
    DisplayLimit "nproc        (-Su)"  processes   "$CUR_NPROC_SOFT"   "$IBM_NPROC_SOFT"   75%

    CheckShellDefaultOptions

    printf "\n"; Message 1019 "${currentusr}"
    printf "\n"; Message 1020
  }


### MacOS systems provide ipcs -T for querying the value of System V
### kernel parameters.  The value string may contain several
### fields, so it is printed as a string for the caller to dissect.

  GetMacOSValue() {
    PARAM=$1
    VALUE=`/usr/bin/ipcs -T 2>/dev/null | grep "${PARAM}:" | awk '{print $2}'`
    printf "$VALUE"
  }


### Analyze the MacOS kernel parameter settings.

  AnalyzeMacOS() {
    IBM_SEMMSL=32
    IBM_SEMMNS=4096
    IBM_SEMOPM=32
    IBM_SEMMNI=128
    IBM_SHMSEG=8
    IBM_SHMMNI=4096
    IBM_SHMALL=2097152
    IBM_SHMMAX=268435456
    IBM_SHMMAX_MIN=33554432
    IBM_FILEMAX=524288
    IBM_NOFILE_HARD=10240
    IBM_NOFILE_SOFT=10240
    IBM_NPROC_HARD=1064
    IBM_NPROC_SOFT=1064

    CUR_SEMMSL=`GetMacOSValue semmsl`
    CUR_SEMMNS=`GetMacOSValue semmns`
    CUR_SEMOPM=`GetMacOSValue semopm`
    CUR_SEMMNI=`GetMacOSValue semmni`
    CUR_SHMSEG=`GetMacOSValue shmseg`
    CUR_SHMMNI=`GetMacOSValue shmmni`
    CUR_SHMALL=`GetMacOSValue shmall`
    CUR_SHMMAX=`GetMacOSValue shmmax`
    CUR_FILEMAX=`/usr/sbin/sysctl -n kern.maxfiles 2>/dev/null`
    CUR_NOFILE_HARD=`ulimit -Hn 2>/dev/null`
    CUR_NOFILE_SOFT=`ulimit -Sn 2>/dev/null`
    CUR_NPROC_HARD=`ulimit -Hu 2>/dev/null`
    CUR_NPROC_SOFT=`ulimit -Su 2>/dev/null`

    printf "\nSystem V Semaphores\n"
    DisplayLimit "semmsl     (sem:1)"  semaphores  "$CUR_SEMMSL"       "$IBM_SEMMSL"
    DisplayLimit "semmns     (sem:2)"  semaphores  "$CUR_SEMMNS"       "$IBM_SEMMNS"       75%
    DisplayLimit "semopm     (sem:3)"  operations  "$CUR_SEMOPM"       "$IBM_SEMOPM"
    DisplayLimit "semmni     (sem:4)"  sets        "$CUR_SEMMNI"       "$IBM_SEMMNI"       75%

    printf "\nSystem V Shared Memory\n"
    DisplayLimit shmseg                segments    "$CUR_SHMSEG"       "$IBM_SHMSEG"       50%
    DisplayLimit shmmax                bytes       "$CUR_SHMMAX"       "$IBM_SHMMAX"       "$IBM_SHMMAX_MIN"
    DisplayLimit shmmni                sets        "$CUR_SHMMNI"       "$IBM_SHMMNI"       75%
    DisplayLimit shmall                pages       "$CUR_SHMALL"       "$IBM_SHMALL"       50%

    printf "\nSystem Settings\n"
    DisplayLimit file-max              files       "$CUR_FILEMAX"      "$IBM_FILEMAX"      75%

    printf "\nCurrent User Limits (%s)\n" "${currentusr}"
    DisplayLimit "nofile       (-Hn)"  files       "$CUR_NOFILE_HARD"  "$IBM_NOFILE_HARD"  75%
    DisplayLimit "nofile       (-Sn)"  files       "$CUR_NOFILE_SOFT"  "$IBM_NOFILE_SOFT"  75%
    DisplayLimit "nproc        (-Hu)"  processes   "$CUR_NPROC_HARD"   "$IBM_NPROC_HARD"   75%
    DisplayLimit "nproc        (-Su)"  processes   "$CUR_NPROC_SOFT"   "$IBM_NPROC_SOFT"   75%

    CheckShellDefaultOptions

    printf "\n"; Message 1019 "${currentusr}"
    printf "\n"; Message 1020
  }


### Initialize variables, counters, and constants used by the script.

  unset MQVER OSVER OPSYS ARCH

  WARNINGS=0
  FAILURES=0

currentusr="`id -un 2>/dev/null`"
platform=`uname`
case ${platform} in
AIX)
    MQ_DEFAULT_INSTALLATION_PATH=/usr/mqm
    unset LD_LIBRARY_PATH
    unset LIBPATH
    ;;

*)
    MQ_DEFAULT_INSTALLATION_PATH=/opt/mqm
    unset LD_LIBRARY_PATH
    ;;
esac

# MQ_INSTALLATION_PATH is modified at install time to be the correct
# directory for the installation if MQ is not installed in the default
# directory.
MQ_INSTALLATION_PATH=$(dirname "$0")/..

  if [ -t 1 ]; then
    PASS="\033[32mPASS\033[m"
    WARN="\033[33mWARN\033[m"
    FAIL="\033[31mFAIL\033[m"
  else
    PASS=PASS
    WARN=WARN
    FAIL=FAIL
  fi

  UNLIMITED=unlimited
  LITERAL=literal
  UNKNOWN="???"
  AUTO=auto

  OPTSTR=":v:"

  case `uname -s` in
    Darwin) OPSYS=MacOS
            ;;

         *) OPSYS=`uname -s`
            ;;
  esac


### Parse the command line to determine the IBM MQ version to analyze.
### Issue an error if there are dangling arguments or no valid IBM MQ
### version was given, but be flexible in allowing different version formats.
### Point WebSphere MQ 6.0 and 5.3 users to the mqconfig-old script.

  while getopts $OPTSTR OPT ; do
    case $OPT in
      \:) Error 1000 "$OPTARG" && PrintSyntax && exit 1
          ;;

      \?) if [ "${OPTARG:-?}" = "?" ]; then
            PrintHelp && exit 0
          else
            Error 1001 $OPTARG && PrintSyntax && exit 1
          fi
          ;;

       v) if [ "${MQVER:=$OPTARG}" != "$OPTARG" ]; then
            PrintSyntax && exit 1
          fi
          ;;
    esac
  done

  if [ $OPTIND -le $# ]; then
    shift `expr $OPTIND - 1`
    Error 1004 "$@" && exit 1
  fi

  MQVER=`printf "%s\n" "$MQVER" | tr -d 'vV' | Trim`

  if [ -z "$MQVER" ]; then
     MQVER=`"${MQ_INSTALLATION_PATH}/bin/dspmqver" -f2 -b | awk -F. '{print $1 "." $2}'`
  fi

  case $MQVER in
      100*|10.0*) MQVER=10.0
                ;;
                
      94*|9.4*) MQVER=9.4
                ;;

      93*|9.3*) MQVER=9.3
                ;;

      92*|9.2*) MQVER=9.2
                ;;

      91*|9.1*) MQVER=9.1
                ;;

      90*|9.0*) MQVER=9.0
                ;;

      8|80*|8.0*) MQVER=8.0
                ;;

      75*|7.5*) MQVER=7.5
                ;;

      71*|7.1*) MQVER=7.1
                ;;

    7|70*|7.0*) MQVER=7.0
                ;;

    6|60*|6.0*) Error 1011 6.0 && exit 1
                ;;

      53*|5.3*) Error 1011 5.3 && exit 1
                ;;

      52*|5.2*) Error 1003 5.2 && exit 1
                ;;

      51*|5.1*) Error 1003 5.1 && exit 1
                ;;

    5|50*|5.0*) Error 1003 5.0 && exit 1
                ;;

            2*) Error 1003 2 && exit 1
                ;;

            1*) Error 1003 1 && exit 1
                ;;

             *) Error 1009 $MQVER && PrintSyntax && exit 1
                ;;
  esac


### Determine the operating system name and version as these will affect
### how the kernel is tuned.  On Linux we list the distribution name but
### do not otherwise check it, but on other platforms we do validate the
### operating system.  On AIX we avoid 'oslevel' when possible since it is
### quite slow.  We query the technology level from the bos.mp64 version and
### avoid checking the service pack except ### on AIX 5.3 where the
### WebSphere MQ V7.0 SOE requires us to do so.

  case `uname -s` in
       AIX) OSVER=`uname -v`
            OSREL=`uname -r`
            ARCH=`uname -p`
            TL=`lslpp -qcL bos.mp64 2>/dev/null | awk -F: '{print $3}' | awk -F. '{print $3}'`

            case $MQVER:$OSVER.$OSREL in
                  10.0:7.3) if [ $TL -lt 1 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 1 $TL && exit 1
                           fi
			   ;;
                  10.0:7.2) if [ $TL -lt 5 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 5 $TL && exit 1
                           fi
			   ;;
                  9.4:7.3) if [ $TL -lt 1 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 1 $TL && exit 1
                           fi
			   ;;
                  9.4:7.2) if [ $TL -lt 5 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 5 $TL && exit 1
                           fi
			   ;;

                  9.3:7.3) ;;
                  9.3:7.2) if [ $TL -lt 3 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 3 $TL && exit 1
                           fi
			   ;;
                  9.2:7.3) ;;
                  9.2:7.2) if [ $TL -lt 3 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 3 $TL && exit 1
                           fi
			   ;;
                  9.2:7.1) if [ $TL -lt 5 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 5 $TL && exit 1
                           fi
			   ;;
                  9.1:7.2) if [ $TL -lt 2 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 2 $TL && exit 1
                           fi
			   ;;
                  9.1:7.1) if [ $TL -lt 5 ]; then
                             Error 1018 $MQVER $OSVER.$OSREL 5 $TL && exit 1
                           fi
			   ;;
                  9.0:7.2) ;;
                  9.0:7.1) if [ $TL -lt 4 ]; then
                             Error 1013 $MQVER $TL && exit 1
                           fi
			   ;;

                  8.0:7.2) ;;
                  8.0:7.1) if [ $TL -lt 2 ]; then
                             Error 1013 $MQVER $TL && exit 1
                           fi
			   ;;

                  8.0:6.1) if [ $TL -lt 8 ]; then
                             Error 1017 $MQVER $TL && exit 1
                           fi
                           ;;

              7.[015]:7.1) ;;

               7.[15]:6.1) if [ $TL -lt 5 ]; then
                             Error 1013 $MQVER $TL && exit 1
                           fi
                           ;;

                  7.0:6.1) ;;

                  7.0:5.3) SP=`oslevel -s | awk -F- '{print $3}'`

                           if [ $TL -lt 4 ]; then
                             Error 1012 && exit 1
                           elif [ $TL -eq 5 -a $SP -lt 2 ]; then
                             Error 1012 && exit 1
                           elif [ $TL -eq 7 -a $SP -lt 1 ]; then
                             Error 1012 && exit 1
                           fi
                           ;;

                        *) Error 1010 $MQVER AIX $OSVER.$OSREL && exit 1
                           ;;
            esac

            Message 1005 "AIX $OSVER.$OSREL TL$TL ($ARCH)" $MQVER
            ;;

     Linux) OSVER=`uname -r`
            ARCH=`uname -p`

            if [ -e /etc/os-release ]; then
               . /etc/os-release
               DIST=${NAME}" "${VERSION}
            elif [ -x /usr/bin/lsb_release ]; then
              DIST=`/usr/bin/lsb_release -sd 2>/dev/null | env LANG=C tr -d \"`
            elif [ -r /etc/redhat-release ]; then
              DIST=`head -1 /etc/redhat-release 2>/dev/null`
            elif [ -r /etc/SuSE-release ]; then
              DIST=`head -1 /etc/SuSE-release 2>/dev/null`
            elif [ -r /etc/UnitedLinux-release ]; then
              DIST=`head -1 /etc/UnitedLinux-release 2>/dev/null`
            else
              DIST=`cat /etc/*-release 2>/dev/null | head -1`
            fi

            Message 1005 "${DIST:=Unknown Linux ($ARCH, $OSVER)}" "$MQVER"
            ;;

    Darwin) OSVER=`uname -r`
            ARCH=`uname -p`

            Message 1005 "Darwin $OSVER ($ARCH)" "$MQVER"
            ;;

         *) Error 1006 `uname -s` && exit 1
            ;;
  esac


### Make sure the binary calculator (bc) is installed before proceeding.
### We rely on bc to handle arithmetic and comparisons since some of the
### values we check can overflow arithmetic precision of the shell.

  printf "quit\n" | bc 1>/dev/null 2>&1  || {
    Error 1016 bc && exit 1
  }


### Call the appropriate function to analyze each operating system.  Set
### the exit status based on the number of failures and warnings.

  eval Analyze$OPSYS

  if [ ${FAILURES:=0} -gt 0 ]; then
    exit 2
  elif [ ${WARNINGS:=0} -gt 0 ]; then
    exit 1
  else
    exit 0
  fi

