[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Patch for kickseed - properly log unsupported command options



Hey all, 

I'd like to propose the following patch to kickseed. While the patch looks like a lot, it is really the same change applied to each command handler (e.g. handlers/auth.sh, handlers/post.sh, handlers/partition.sh). The problem is that none of the kickseed handlers correctly log messages about unsupported handler options. 

All the handlers currently parse options like so:  

          eval set -- "$(getopt -o '' -l interpreter: -- "$@")" || { warn_getopt %pre; return; }

As mentioned in some bash docs (/usr/share/doc/util-linux-ng-2.17.2/getopt-parse.bash), this will *never* call warn_getopt because eval set swallows the return code. This is true of ash as well. 

Because of this, almost every bug report I've seen mentions something along the lines of "getopt printed an error". This is getout outputting to stderr, but this only shows briefly -- there is nothing printed into any log. Proper behavior would be for an unknown option to be printed to stderr (as it is now) and for a log message to be generated (which is what this patch adds). 

FWIW, this "swallow" behavior was likely designed intentionally to disable automatic exit (errexit) whenever getopt found an unexpected argument, due to the fact that kickseed supports so few options and therefore this "error" happens on almost every kickstart file. However, it looks like the code was intended to print to both the console and to the log file, and in that respect it's failing. 

The patch adds a new function parse_options to kickseed.sh that briefly disables errexit, runs getopt, reenables errexit, and then prints a warning message to the log if there was an error. As ash doesn't support ERR trapping, I capture the stderr output and print a log message if it's not empty. To do this, I have to run getopt twice, but that's a small price to pay for good logging. This doesn't affect the current  console behavior at all - a warning is still printed to the tty as the install is running.  I've updated each handler, they now check options as so: 

   eval set -- "$(parse_options %pre interpreter: "$@")"

The patch result is that /var/log/installer/syslog now contains entries such as this for unknown options:

  Jun 28 23:49:04 kickseed: Failed to parse some %pre options
  Jun 28 23:49:04 kickseed: getopt: unrecognized option '--log'

This makes it much easier to debug an installation that didn't go quite as expected. Feedback and input welcome, if this gets merged in I'm happy to submit my next series of changes. 

Best, 
Hamilton


diff --git handlers/auth.sh handlers/auth.sh
index 530ef71..0e8ce33 100644
--- handlers/auth.sh
+++ handlers/auth.sh
@@ -5,7 +5,7 @@ auth_handler () {
  # --enableldaptls, --enablekrb5, --krb5realm=, --krb5kdc=,
  # --krb5adminserver=, --enablehesiod, --hesiodlhs,
  # --hesiodrhs, --enablesmbauth, --smbservers=, --smbworkgroup=
- eval set -- "$(getopt -o '' -l enablemd5,enablenis,nisdomain:,nisserver:,useshadow,enableshadow,enablecache -- "$@")" || { warn_getopt auth; return; }
+ eval set -- "$(parse_options auth enablemd5,enablenis,nisdomain:,nisserver:,useshadow,enableshadow,enablecache "$@")"
  while :; do
    case $1 in
      --enablemd5)
diff --git handlers/bootloader.sh handlers/bootloader.sh
index 33b51d5..dd74b55 100644
--- handlers/bootloader.sh
+++ handlers/bootloader.sh
@@ -4,7 +4,7 @@ bootloader_handler_common () {
  useLilo="$1"
  shift
  # TODO --linear, --nolinear, --lba32
- eval set -- "$(getopt -o '' -l location:,password:,md5pass:,useLilo,upgrade -- "$@")" || { warn_getopt bootloader; return; }
+ eval set -- "$(parse_options bootloader location:,password:,md5pass:,useLilo,upgrade "$@")"
  while :; do
    case $1 in
      --location)
diff --git handlers/clearpart.sh handlers/clearpart.sh
index c2b17d9..f6f5ebb 100644
--- handlers/clearpart.sh
+++ handlers/clearpart.sh
@@ -4,7 +4,7 @@ clearpart_method=
 clearpart_need_method=
 
 clearpart_handler () {
- eval set -- "$(getopt -o '' -l linux,all,drives:,initlabel -- "$@")" || { warn_getopt clearpart; return; }
+ eval set -- "$(parse_options clearpart linux,all,drives:,initlabel "$@")"
  while :; do
    case $1 in
      --linux)
diff --git handlers/device.sh handlers/device.sh
index 49e595c..c9265e5 100644
--- handlers/device.sh
+++ handlers/device.sh
@@ -2,7 +2,7 @@
 
 device_handler () {
  opts=
- eval set -- "$(getopt -o '' -l opts: -- "$@")" || { warn_getopt device; return; }
+ eval set -- "$(parse_options device opts: "$@")"
  while :; do
    case $1 in
      --opts)
diff --git handlers/firewall.sh handlers/firewall.sh
index c02f427..908bed1 100644
--- handlers/firewall.sh
+++ handlers/firewall.sh
@@ -1,7 +1,7 @@
 #! /bin/sh
 
 firewall_handler () {
- eval set -- "$(getopt -o '' -l high,medium,enabled,enable,disabled,disable,trust:,dhcp,ssh,telnet,smtp,http,ftp,port: -- "$@")" || { warn_getopt firewall; return; }
+ eval set -- "$(parse_options firewall high,medium,enabled,enable,disabled,disable,trust:,dhcp,ssh,telnet,smtp,http,ftp,port: "$@")"
  while :; do
    case $1 in
      --high|--medium|--enabled|--enable|--dhcp|--ssh|--telnet|--smtp|--http|--ftp)
diff --git handlers/langsupport.sh handlers/langsupport.sh
index 192a119..63f6272 100644
--- handlers/langsupport.sh
+++ handlers/langsupport.sh
@@ -3,7 +3,7 @@
 langsupport_default=
 
 langsupport_handler () {
- eval set -- "$(getopt -o '' -l default: -- "$@")" || { warn_getopt langsupport; return; }
+ eval set -- "$(parse_options langsupport default: "$@")"
  while :; do
    case $1 in
      --default)
diff --git handlers/logvol.sh handlers/logvol.sh
index 6c1ac7e..4c586f2 100644
--- handlers/logvol.sh
+++ handlers/logvol.sh
@@ -13,7 +13,7 @@ logvol_handler () {
  fstype=
 
  # TODO --percent=, --bytes-per-inode=, --fsoptions=
- eval set -- "$(getopt -o '' -l vgname:,recommended,size:,grow,maxsize:,name:,noformat,fstype: -- "$@")" || { warn_getopt logvol; return; }
+ eval set -- "$(parse_options logvol vgname:,recommended,size:,grow,maxsize:,name:,noformat,fstype: "$@")"
  while :; do
    case $1 in
      --vgname)
diff --git handlers/mouse.sh handlers/mouse.sh
index ea1fd9d..0fab4fb 100644
--- handlers/mouse.sh
+++ handlers/mouse.sh
@@ -6,7 +6,7 @@ mouse_handler () {
    return
  fi
 
- eval set -- "$(getopt -o '' -l device:,emulthree -- "$@")" || { warn_getopt mouse; return; }
+ eval set -- "$(parse_options mouse device:,emulthree "$@")"
  while :; do
    case $1 in
      --device)
diff --git handlers/network.sh handlers/network.sh
index b50ba40..dda1b19 100644
--- handlers/network.sh
+++ handlers/network.sh
@@ -5,7 +5,7 @@ network_handler () {
  got_gateway=
  got_nameservers=
  got_netmask=
- eval set -- "$(getopt -o '' -l bootproto:,device:,ip:,gateway:,nameserver:,nodns,netmask:,hostname: -- "$@")" || { warn_getopt network; return; }
+ eval set -- "$(parse_options network bootproto:,device:,ip:,gateway:,nameserver:,nodns,netmask:,hostname: "$@")"
  while :; do
    case $1 in
      --bootproto)
diff --git handlers/partition.sh handlers/partition.sh
index b3da6f4..803a6c1 100644
--- handlers/partition.sh
+++ handlers/partition.sh
@@ -21,7 +21,7 @@ partition_handler () {
  asprimary=
  fstype=
 
- eval set -- "$(getopt -o '' -l recommended,size:,grow,maxsize:,noformat,onpart:,usepart:,ondisk:,ondrive:,asprimary,fstype:,start:,end: -- "$@")" || { warn_getopt partition; return; }
+ eval set -- "$(parse_options partition recommended,size:,grow,maxsize:,noformat,onpart:,usepart:,ondisk:,ondrive:,asprimary,fstype:,start:,end: "$@")"
  while :; do
    case $1 in
      --recommended)
diff --git handlers/post.sh handlers/post.sh
index a6e637f..d022f43 100644
--- handlers/post.sh
+++ handlers/post.sh
@@ -5,7 +5,7 @@ post_handler_section () {
  post_chroot=1
  post_interpreter=
 
- eval set -- "$(getopt -o '' -l nochroot,interpreter: -- "$@")" || { warn_getopt %post; return; }
+ eval set -- "$(parse_options %post nochroot,interpreter: "$@")"
  while :; do
    case $1 in
      --nochroot)
diff --git handlers/pre.sh handlers/pre.sh
index fa03a80..7f8358c 100644
--- handlers/pre.sh
+++ handlers/pre.sh
@@ -1,7 +1,8 @@
 #! /bin/sh
 
 pre_handler_section () {
- eval set -- "$(getopt -o '' -l interpreter: -- "$@")" || { warn_getopt %pre; return; }
+
+ eval set -- "$(parse_options %pre interpreter: "$@")"
  while :; do
    case $1 in
      --interpreter)
diff --git handlers/preseed.sh handlers/preseed.sh
index c2d99c2..9bbecb0 100644
--- handlers/preseed.sh
+++ handlers/preseed.sh
@@ -3,7 +3,7 @@
 preseed_handler () {
  owner=d-i
 
- eval set -- "$(getopt -o '' -l owner: -- "$@")" || { warn_getopt preseed; return; }
+ eval set -- "$(parse_options preseed owner: "$@")"
  while :; do
    case $1 in
      --owner)
diff --git handlers/repo.sh handlers/repo.sh
index 2697e14..6e0aed0 100644
--- handlers/repo.sh
+++ handlers/repo.sh
@@ -11,7 +11,7 @@ repo_handler () {
  key=
 
  # TODO --mirrorlist=
- eval set -- "$(getopt -o '' -l name:,baseurl:,distribution:,components:,source,key: -- "$@")" || { warn_getopt repo; return; }
+ eval set -- "$(parse_options repo name:,baseurl:,distribution:,components:,source,key: "$@")"
  while :; do
    case $1 in
      --name)
diff --git handlers/rootpw.sh handlers/rootpw.sh
index f375cd2..c6deab4 100644
--- handlers/rootpw.sh
+++ handlers/rootpw.sh
@@ -4,7 +4,7 @@ rootpw_handler () {
  setpassword=1
  crypted=
 
- eval set -- "$(getopt -o '' -l disabled,iscrypted -- "$@")" || { warn_getopt rootpw; return; }
+ eval set -- "$(parse_options rootpw disabled,iscrypted "$@")"
  while :; do
    case $1 in
      --disabled)
diff --git handlers/timezone.sh handlers/timezone.sh
index d09bb04..f34a542 100644
--- handlers/timezone.sh
+++ handlers/timezone.sh
@@ -3,7 +3,7 @@
 timezone_handler () {
  utc=
 
- eval set -- "$(getopt -o '' -l utc -- "$@")" || { warn_getopt timezone; return; }
+ eval set -- "$(parse_options timezone utc "$@")"
  while :; do
    case $1 in
      --utc)
diff --git handlers/url.sh handlers/url.sh
index 05b2404..d1f6d06 100644
--- handlers/url.sh
+++ handlers/url.sh
@@ -1,7 +1,7 @@
 #! /bin/sh
 
 url_handler () {
- eval set -- "$(getopt -o '' -l url: -- "$@")" || { warn_getopt url; return; }
+ eval set -- "$(parse_options url url: "$@")"
  while :; do
    case $1 in
      --url)
diff --git handlers/user.sh handlers/user.sh
index d11b381..2577c4b 100644
--- handlers/user.sh
+++ handlers/user.sh
@@ -5,7 +5,7 @@ user_handler () {
  crypted=
  password=
 
- eval set -- "$(getopt -o '' -l disabled,fullname:,iscrypted,password: -- "$@")" || { warn_getopt user; return; }
+ eval set -- "$(parse_options user disabled,fullname:,iscrypted,password: "$@")"
  while :; do
    case $1 in
      --disabled)
diff --git handlers/volgroup.sh handlers/volgroup.sh
index b3a4ea5..ee33146 100644
--- handlers/volgroup.sh
+++ handlers/volgroup.sh
@@ -34,7 +34,7 @@ volgroup_pull_recipe () {
 volgroup_handler () {
  existing=
 
- eval set -- "$(getopt -o '' -l noformat,useexisting,pesize: -- "$@")" || { warn_getopt volgroup; return; }
+ eval set -- "$(parse_options volgroup noformat,useexisting,pesize: "$@")"
  while :; do
    case $1 in
      --noformat|--useexisting)
diff --git handlers/xconfig.sh handlers/xconfig.sh
index 43f064e..8e8bd94 100644
--- handlers/xconfig.sh
+++ handlers/xconfig.sh
@@ -1,7 +1,7 @@
 #! /bin/sh
 
 xconfig_handler () {
- eval set -- "$(getopt -o '' -l noprobe,card:,videoram:,monitor:,hsync:,vsync:,defaultdesktop:,startxonboot,resolution:,depth: -- "$@")" || { warn_getopt xconfig; return; }
+ eval set -- "$(parse_options xconfig noprobe,card:,videoram:,monitor:,hsync:,vsync:,defaultdesktop:,startxonboot,resolution:,depth: "$@")"
  while :; do
    case $1 in
      --noprobe)
diff --git kickseed.sh kickseed.sh
index ccc65dd..476ccc1 100644
--- kickseed.sh
+++ kickseed.sh
@@ -24,7 +24,7 @@ die () {
 }
 
 warn_getopt () {
- warn "Failed to parse $1 options"
+ warn "Failed to parse some $1 options"
 }
 
 warn_bad_opt () {
@@ -35,6 +35,30 @@ warn_bad_arg () {
  warn "Unimplemented $1 $2 argument $3"
 }
 
+# Used by HANDLERS for parsing command options
+#
+# Handles unknown options by outputting errors to stderr and
+# syslog. Important because we only handle a few options, so
+# users need a good log if an install didn't complete as
+# expected
+parse_options () {
+ type=$1
+ options=$2
+ shift; shift;
+
+ # Disable errexit so we can handle it specially
+ set +e
+ output=$(getopt -o '' -l $options -- "$@")
+ errout=$(getopt -o '' -l $options -- "$@" 2>&1 >/dev/null)
+ set -e
+
+ if [[ -n "$errout" ]]; then
+   warn_getopt $type
+   warn "$type Parse Error: $errout"
+ fi
+ echo "$output"
+}
+
 # Allow handler files to register functions to be called at the end of
 # processing; this lets them build up preseed answers over several handler
 # calls.
@@ -122,7 +146,7 @@ kickseed () {
      echo "$line"
    fi
  done < "$1"; echo %final) | while read -r line; do
-   # Work out the section.
+   # Check if we've reached a new section
    keyword="${line%%[  ]*}"
    if [ "$keyword" = '%packages' ]; then
      save_post_script
@@ -151,6 +175,8 @@ kickseed () {
      break
    fi
 
+   # We are inside a section. Based on what section
+   # we are in, evaluate the current line
    if [ "$SECTION" = main ]; then
      if [ -z "$keyword" ] || [ "${keyword#\#}" != "$keyword" ]; then
        # Ignore empty lines and comments.


Reply to: