dash.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Steffen Nurpmeso <steffen@sdaoden.eu>
To: DASH shell mailing list <dash@vger.kernel.org>,
	Denys Vlasenko <vda.linux@googlemail.com>,
	Jilles Tjoelker <jilles@stack.nl>
Subject: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think
Date: Sat, 19 Dec 2020 18:28:38 +0100	[thread overview]
Message-ID: <20201219172838.1B-WB%steffen@sdaoden.eu> (raw)

[-- Attachment #1: Type: text/plain, Size: 1149 bytes --]

Hello.

Long story short, after falsely accusing BSD make of not working
when child shells use monitor mode someone pointed me into the
correct direction, and whereas the major problem was on the mksh
side (that sneaked in via $SHELL even though .. but regardless)
and was already reported, i can report a minor issue with the
mentioned shells nonetheless: with the reproducer (please do not
complain, it tooks hours to get it that short), when run under
supervision of a(ny tested) make(1), they spring a TTOU trap even
though they should not .. i think.

Please run via "make -f jobo.mk SHELL=YOUR-SHELL", and you should
see something like

  ./mx-test.sh
  Starting job reaper (timeout of 2 seconds)
  .. waiting for job reaper to come up
  TTOU
  ....

where this TTOU should not happen i would say.  It does not happen
with bash nor mksh nor OpenBSD sh and ksh, nor NetBSD sh and eh,
ksh, hmm.

I hope this helps, a nice weekend from Germany i wish,

--steffen
|
|Der Kragenbaer,                The moon bear,
|der holt sich munter           he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)

[-- Attachment #2: mx-test.sh --]
[-- Type: application/x-sh, Size: 10098 bytes --]

#!/bin/sh -

OBJDIR='/tmp/jobo';export OBJDIR # MANAGED!!

: ${SHELL:=/bin/sh};export SHELL

: ${JOBNO:=1}
: ${JOBWAIT:=2}
: ${JOBMON:=y}

export \
   OBJDIR SHELL JOBNO JOBWAIT JOBMON

LC_ALL=C LANG=C TZ=UTC
export LC_ALL LANG TZ

if [ -z "${MAILX__CC_TEST_RUNNING}" ]; then
      CHECK=1 RUN_TEST= MAILX=
   export CHECK RUN_TEST MAILX
fi

awk='/usr/bin/awk';export awk
cat='/bin/cat';export cat
grep='/usr/bin/grep';export grep
mkdir='/bin/mkdir';export mkdir
rm='/bin/rm';export rm

if [ -z "${MAILX__CC_TEST_RUNNING}" ]; then
   MAILX__CC_TEST_RUNNING=y
   export MAILX__CC_TEST_RUNNING
rm -rf $OBJDIR
mkdir -p $OBJDIR
   exec "${SHELL}" "${0}" "${@}"
fi
cd $OBJDIR || exit 100

DEVELDIFF= DUMPERR=
TESTS_PERFORMED=0 TESTS_OK=0 TESTS_FAILED=0 TESTS_SKIPPED=0
JOBS=0 JOBLIST= JOBDESC= JOBREAPER= JOBSYNC=
SUBSECOND_SLEEP=
   ( sleep .1 ) >/dev/null 2>&1 && SUBSECOND_SLEEP=y

   TESTS_NET_TEST=
   [ "${OPT_NET_TEST}" = 1 ] && [ -x ./net-test ] && TESTS_NET_TEST=1
   export TESTS_NET_TEST

COLOR_ERR_ON= COLOR_ERR_OFF=  COLOR_DBGERR_ON= COLOR_DBGERR_OFF=
COLOR_WARN_ON= COLOR_WARN_OFF=
COLOR_OK_ON= COLOR_OK_OFF=
ESTAT=0
TEST_NAME=

trap "
   jobreaper_stop
" EXIT
trap "exit 1" HUP INT QUIT TERM
trap 'echo TTIN' TTIN
trap 'echo TTOU' TTOU
trap 'echo TSTP' TSTP
trap : CHLD

jobreaper_start() {
   printf 'Starting job reaper (timeout of %s seconds)\n' ${JOBWAIT}

   i=
   trap 'i=1' USR1 # "reaper (actually a notify timer only) is up"
   (
      parent=${$}
      sleeper= int=0 hot=0
      trap '' EXIT HUP QUIT CHLD
      trap 'exit 0' INT
      trap '
         [ -n "${sleeper}" ] && kill -TERM ${sleeper} >/dev/null 2>&1
         int=1 hot=1
      ' USR1
      trap '
         [ -n "${sleeper}" ] && kill -TERM ${sleeper} >/dev/null 2>&1
         int=1 hot=0
      ' USR2
      trap '
         [ -n "${sleeper}" ] && kill -TERM ${sleeper} >/dev/null 2>&1
         echo "Stopping job reaper"
         exit 0
      ' TERM

      # traps are setup, notify parent that we are up and running
      kill -USR1 ${parent} >/dev/null 2>&1

      while :; do
         int=0
         sleep ${JOBWAIT} &
         sleeper=${!}
         wait ${!}
         sleeper=
         [ "${int}${hot}" = 01 ] && kill -USR1 ${parent} >/dev/null 2>&1
      done
   ) </dev/null & #>/dev/null 2>&1 &
   JOBREAPER=${!}

   j=
   if [ ${?} -eq 0 ]; then
      while :; do
         if [ -n "${i}" ]; then
            trap '' USR1
            return
         fi
         printf '..%s waiting for job reaper to come up\n' "${j}"
         j=' still'
         sleep 1 &
         wait ${!}
      done
   fi

   JOBREAPER=
   printf '%s!! Cannot start the wild job reaper!%s\n' \
      "${COLOR_ERR_ON}" "${COLOR_ERR_OFF}"
}

jobreaper_stop() {
   [ -n "${JOBREAPER}" ] && kill -TERM ${JOBREAPER} >/dev/null 2>&1
   JOBREAPER=
   if [ ${JOBS} -gt 0 ]; then
      echo 'Cleaning up running jobs'
      jtimeout
      wait ${JOBLIST}
      JOBLIST=
   fi
}

jspawn() {
   if [ ${JOBNO} -gt 1 ]; then
      # We are spawning multiple jobs..
      [ ${JOBS} -eq 0 ] && printf '...'
      JOBS=`add ${JOBS} 1`
      printf ' [%s=%s]' ${JOBS} "${1}"
   else
      JOBS=1
      # Assume problems exist, do not let user keep hanging on terminal
      if [ -n "${RUN_TEST}" ]; then
         printf '... [%s]\n' "${1}"
      fi
   fi

   [ -n "${JOBMON}" ] && set -m >/dev/null 2>&1
   (  # Place the job in its own directory to ease file management
      trap '' EXIT HUP INT QUIT TERM USR1 USR2
      ${mkdir} t.${JOBS}.d && cd t.${JOBS}.d &&
         eval t_${1} ${JOBS} ${1} &&
         ${rm} -f ../t.${JOBS}.id
   ) > t.${JOBS}.io </dev/null & # 2>&1 </dev/null &
   i=${!}
   [ -n "${JOBMON}" ] && set +m >/dev/null 2>&1
   JOBLIST="${JOBLIST} ${i}"
   printf '%s\n%s\n' ${i} ${1} > t.${JOBS}.id

   # ..until we should sync or reach the maximum concurrent number
   [ ${JOBS} -lt ${JOBNO} ] && return

   jsync 1
}

jsync() {
   if [ ${JOBS} -eq 0 ]; then
      [ -n "${TEST_ANY}" ] && printf '\n'
      TEST_ANY=
      return
   fi
   [ -z "${JOBSYNC}" ] && [ ${#} -eq 0 ] && return

   [ ${JOBNO} -ne 1 ] && printf ' .. waiting\n'

   if [ -n "${JOBREAPER}" ]; then
      timeout= alldone=

      trap 'echo TIMEOUT IN PARENT;timeout=1' USR1
      kill -USR1 ${JOBREAPER} >/dev/null 2>&1

      loops=0
      while [ -z "${timeout}" ]; do
         alldone=1
         i=0
         while [ ${i} -lt ${JOBS} ]; do
            i=`add ${i} 1`
            [ -f t.${i}.id ] || continue
            alldone=
            break
         done
         [ -n "${alldone}" ] && break

         if [ -z "${SUBSECOND_SLEEP}" ]; then
            loops=`add ${loops} 1`
            [ ${loops} -lt 111 ] && continue
            sleep 1 &
         else
            sleep .5 &
         fi
echo >&2 PREWAIT
         wait ${!}
      done

      kill -USR2 ${JOBREAPER} >/dev/null 2>&1
      trap '' USR1

      [ -n "${timeout}" ] && jtimeout
   fi

   # Now collect the zombies
   wait ${JOBLIST}
   JOBLIST=

   # Update global counters
   i=0
   while [ ${i} -lt ${JOBS} ]; do
      i=`add ${i} 1`

      if [ -f t.${i}.id ]; then
         { read pid; read desc; } < t.${i}.id
         desc=${desc#${desc%%[! ]*}}
         desc=${desc%${desc##*[! ]}}
         [ -s t.${i}.io ] && printf >&2 '\n'
         printf >&2 '%s!! Timeout: reaped job %s [%s]%s\n' \
            "${COLOR_ERR_ON}" ${i} "${desc}" "${COLOR_ERR_OFF}"
         TESTS_FAILED=`add ${TESTS_FAILED} 1`
      elif [ -s t.${i}.result ]; then
         read es tp to tf ts < t.${i}.result
         TESTS_PERFORMED=`add ${TESTS_PERFORMED} ${tp}`
         TESTS_OK=`add ${TESTS_OK} ${to}`
         TESTS_FAILED=`add ${TESTS_FAILED} ${tf}`
         TESTS_SKIPPED=`add ${TESTS_SKIPPED} ${ts}`
         [ "${es}" != 0 ] && ESTAT=${es}
      else
         TESTS_FAILED=`add ${TESTS_FAILED} 1`
         ESTAT=1
      fi
   done

   JOBS=0
}

jtimeout() {
   i=0
   while [ ${i} -lt ${JOBS} ]; do
      i=`add ${i} 1`
      if [ -f t.${i}.id ] &&
            read pid < t.${i}.id >/dev/null 2>&1 &&
            kill -0 ${pid} >/dev/null 2>&1; then
         j=${pid}
         [ -n "${JOBMON}" ] && j=-${j}
         kill -KILL ${j} >/dev/null 2>&1
      else
         ${rm} -f t.${i}.id
      fi
   done
}

t_prolog() {
   shift

   ESTAT=0 TESTS_PERFORMED=0 TESTS_OK=0 TESTS_FAILED=0 TESTS_SKIPPED=0 \
      TEST_NAME=${1} TEST_ANY=

   printf '%s[%s]%s\n' "" "${TEST_NAME}" ""
}

t_epilog() {
   [ -n "${TEST_ANY}" ] && printf '\n'

   printf '%s %s %s %s %s\n' \
      ${ESTAT} \
         ${TESTS_PERFORMED} ${TESTS_OK} ${TESTS_FAILED} ${TESTS_SKIPPED} \
      > ../t.${1}.result
}

t_echo() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s' "${*}"
   TEST_ANY=1
}

t_echook() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s%s:ok%s' "${COLOR_OK_ON}" "${*}" "${COLOR_OK_OFF}"
   TEST_ANY=1
}

t_echoerr() {
   ESTAT=1
   t_echo0err "${@}"
}

t_echo0err() {
   [ -n "${TEST_ANY}" ] && __i__="\n" || __i__=
   printf "${__i__}"'%sERROR: %s%s\n' \
      "${COLOR_ERR_ON}" "${*}" "${COLOR_ERR_OFF}"
   TEST_ANY=
}

t_echowarn() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s%s%s' "${COLOR_WARN_ON}" "${*}" "${COLOR_WARN_OFF}"
   TEST_ANY=1
}

t_echoskip() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s%s[skip]%s' \
      "${COLOR_WARN_ON}" "${*}" "${COLOR_WARN_OFF}"
   TEST_ANY=1
   TESTS_SKIPPED=`add ${TESTS_SKIPPED} 1`
}

check() {
   restat=${?} tid=${1} eestat=${2} f=${3} s=${4} optmode=${5}

echo >&2 CHECK $TID 0
   TESTS_PERFORMED=`add ${TESTS_PERFORMED} 1`

echo >&2 CHECK $TID 10
   if [ -n "${CHECK}${RUN_TEST}" ]; then
      x="t.${TEST_NAME}-${tid}"
echo >&2 CHECK $TID 11
      if :; then
         y=test-out
echo >&2 CHECK $TID 20
echo >&2 CHECK $TID 21
         if [ -n "${y}" ]; then
echo >&2 CHECK $TID 22
            if : ; then
echo >&2 CHECK $TID 22.5
# NOTE: REMOVE THIS (or use true(1)) AND WORKS directly and via bmake (mksh)
# (env -i regardless, "false" also is not it, whatever)
env -i ls -latr /NONEXISTENT
echo >&2 CHECK $SHELL $TID 22.8
# NOTE: monitor should be "on" here
set -o >&2
echo >&2 CHECK $TID 23
            fi
echo >&2 CHECK $TID 30
         fi
echo >&2 CHECK $TID 35
      fi
   fi
echo >&2 CHECK $TID 100
}

check_ex0() {
   # $1=test name [$2=status]
   __qm__=${?}
   [ ${#} -gt 1 ] && __qm__=${2}

   TESTS_PERFORMED=`add ${TESTS_PERFORMED} 1`

   if [ ${__qm__} -ne 0 ]; then
      ESTAT=1
      t_echoerr "${1}: unexpected non-0 exit status: ${__qm__}"
      TESTS_FAILED=`add ${TESTS_FAILED} 1`
   else
      t_echook "${1}"
      TESTS_OK=`add ${TESTS_OK} 1`
   fi
}

check_exn0() {
   # $1=test name [$2=status]
   __qm__=${?}
   [ ${#} -gt 1 ] && __qm__=${2}
   [ ${#} -gt 2 ] && __expect__=${3} || __expect__=

   TESTS_PERFORMED=`add ${TESTS_PERFORMED} 1`

   if [ ${__qm__} -eq 0 ]; then
      ESTAT=1
      t_echoerr "${1}: unexpected 0 exit status: ${__qm__}"
      TESTS_FAILED=`add ${TESTS_FAILED} 1`
   elif [ -n "${__expect__}" ] && [ ${__expect__} -ne ${__qm__} ]; then
      ESTAT=1
      t_echoerr "${1}: unexpected exit status: ${__qm__} != ${__expected__}"
      TESTS_FAILED=`add ${TESTS_FAILED} 1`
   else
      t_echook "${1}"
      TESTS_OK=`add ${TESTS_OK} 1`
   fi
}

color_init() {
   :
}

if ( [ "$((1 + 1))" = 2 ] ) >/dev/null 2>&1; then
   add() {
      echo "$((${1} + ${2}))"
   }
else
   add() {
      ${awk} 'BEGIN{print '${1}' + '${2}'}'
   }
fi

if ( [ "$((2 % 3))" = 2 ] ) >/dev/null 2>&1; then
   modulo() {
      echo "$((${1} % ${2}))"
   }
else
   modulo() {
      ${awk} 'BEGIN{print '${1}' % '${2}'}'
   }
fi

have_feat() {
   ( "${RAWMAILX}" ${ARGS} -X'echo $features' -Xx |
      ${grep} +${1}, ) >/dev/null 2>&1
}

t_X_errexit() {
   t_prolog "${@}"

   </dev/null ${MAILX} ${ARGS} -Snomemdebug \
         -X'echo one' -X' echos nono ' -X'echo two' \
      > ./t1 2>&1
echo >&2 PRE T1 CHEK
   check 1 0 ./t1 '2700500141 51'
echo >&2 AFTER T1 CHEK
t_epilog "$@"
return
}

ssec=$SECONDS
      jobreaper_start
   jspawn X_errexit
jsync 1
      jobreaper_stop
esec=$SECONDS

exit ${ESTAT}
# s-sh-mode

[-- Attachment #3: jobo.mk --]
[-- Type: text/plain, Size: 20 bytes --]

test:
	./mx-test.sh

             reply	other threads:[~2020-12-19 17:35 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-19 17:28 Steffen Nurpmeso [this message]
2020-12-19 22:21 ` Steffen Nurpmeso
2020-12-19 23:52   ` Harald van Dijk
2020-12-21 16:24     ` Jilles Tjoelker
2020-12-21 19:43       ` Steffen Nurpmeso
2020-12-23 20:18       ` Harald van Dijk
2020-12-24 15:29         ` Jilles Tjoelker
2021-01-10 23:56         ` Harald van Dijk
2021-01-06  4:46       ` Herbert Xu
2021-01-06  4:45     ` [PATCH] jobs: Block signals during tcsetpgrp Herbert Xu
2021-01-06 21:16       ` Harald van Dijk
2021-01-06 22:41         ` Jilles Tjoelker
2021-01-07  7:36         ` Denys Vlasenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201219172838.1B-WB%steffen@sdaoden.eu \
    --to=steffen@sdaoden.eu \
    --cc=dash@vger.kernel.org \
    --cc=jilles@stack.nl \
    --cc=vda.linux@googlemail.com \
    --subject='Re: dash 0.5.11.2, busybox sh 1.32.0, FreeBSD 12.2 sh: spring TTOU but should not i think' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).