#!/bin/bash # $HeadURL: https://svn.pasta.freemyip.com/main/miniade/trunk/bin/nop-sh $ $LastChangedRevision: 10133 $ # Modules . $(miniade) || { echo "${0##*/}: ERROR: miniade failed (hint: run 'miniade' to see error)" >&2; exit 1; } # Configurable stuff SOLVER=solver7 WORD_LENGTH=5 WORD_FILE=../etc/words-$WORD_LENGTH.txt # Other globals GGGGG_RESPONSE=$(printf "%$WORD_LENGTH.${WORD_LENGTH}s\\n" gggggggggggggggggggggg) main() { local MY_ARGS # Process options miniade_process_options MY_ARGS "$@" && set -- "${MY_ARGS[@]}" # Process arguments [ $# = 0 ] || usage # Sanity checks and derivations U2C_FIFO=/tmp/$PROGNAME.$$.u2c-fifo C2U_FIFO=/tmp/$PROGNAME.$$.c2u-fifo # Guts miniade_debug 10 "main: starting server ..." start_server trap "stop_server 2>/dev/null" 0 miniade_debug 10 "main: setting up bidirection communication handles ..." rm -f $U2C_FIFO $C2U_FIFO mkfifo $U2C_FIFO $C2U_FIFO # It's important that the handle allocation and exec are done # together: if *both* the handle allocations are done first # and *then* *both* the execs then the IO gets mangled! FD0=$(comm --nocheck-order -2 -3 <(echo {0..255} | xargs -n 1 echo | sort -n) <(ls /proc/$$/fd | sort -n) | head -1) eval "exec $FD0<>$U2C_FIFO" FD1=$(comm --nocheck-order -2 -3 <(echo {0..255} | xargs -n 1 echo | sort -n) <(ls /proc/$$/fd | sort -n) | head -1) eval "exec $FD1<>$C2U_FIFO" miniade_debug 10 "main: entering main loop ..." CLIENT_PID=rubbish while :; do if [ ! -d /proc/$CLIENT_PID ]; then miniade_info "(re)starting client ..." eval "start_client <&$FD0 >&$FD1" & CLIENT_PID=$! miniade_debug 10 "main: client started with pid $CLIENT_PID" fi miniade_debug 10 "main: awaiting/reading guess from client ..." eval "read GUESS <&$FD1" miniade_debug 10 "main: sending guess ($GUESS) to user ..." echo "$GUESS" # Interaction between this wrapper script and the solver becomes # a bit erratic if we don't follow the correct conversation # protocols exactly. For this reason it is better if we handle # abberations in this script rather than passing them to the # solver. BREAK_OUT_OF_OUTER_LOOP_FLAG=false while :; do miniade_debug 10 "main: awaiting/reading response from user ..." if ! read RESPONSE; then BREAK_OUT_OF_OUTER_LOOP_FLAG=true # This is the case that triggered the addition of this # inner loop: when the user just presses enter without # entering a response. elif [ "X$RESPONSE" = X ]; then miniade_warning "empty input ignored" continue fi # By default this inner loop is one time only. break done if $BREAK_OUT_OF_OUTER_LOOP_FLAG; then break fi miniade_debug 10 "main: sending response ($RESPONSE) to client ..." eval "echo \"$RESPONSE\" >&$FD0" # If the response was 'ggggg' then the client will # exit. We don't want to loop round into the # is-client-still-running code before the client has # had a chance to exit. However, we can do the sleep # only when the response was 'ggggg', which avoids # sleeping each and every loop. if [ "X$RESPONSE" = "X$GGGGG_RESPONSE" ]; then sleep 1 fi done kill $CLIENT_PID miniade_debug 10 "stop_server: waiting for client (pid $CLIENT_PID) to exit ..." wait $CLIENT_PID miniade_debug 10 "main: wait returned $?" miniade_debug 10 "main: stopping server ..." stop_server miniade_debug 10 "main: removing fifos ..." rm -f $U2C_FIFO $C2U_FIFO miniade_debug 10 "main: exiting ..." } start_server() { $SOLVER --server-start --length=$WORD_LENGTH & SERVER_PID=$! miniade_debug 10 "start_server: server started with pid $SERVER_PID" sleep 3 } stop_server() { $SOLVER --server-stop --length=$WORD_LENGTH miniade_debug 10 "stop_server: waiting for server (pid $SERVER_PID) to exit ..." wait $SERVER_PID miniade_debug 10 "stop_server: wait returned $?" } start_client() { # Final solving summary line needs to be redirected, else # send/receive/.../send/receive becomes # send/receive/.../send/receive/receive, which is harder # for this wrapper to handle. # # Note the exec!!! This is because start_client() is called # above with: # # eval "start_client <&$FD0 >&$FD1" & # # and those redirections mean that the bash process running # this script will fork another *bash* process to do all # the interpretation of those redirections! (Then *that* # bash script will *call* (not exec) the commands below, # all of which results in the $$ - as seen immediately # after the eval call - being the *bash* process that # is the parent of the solver. So when we come to kill # the solver, actually, we kill the bash calling the solver, # but not the solver itself. So it is for *this* reason # that we use exec: so the bash process that is $$ is # *replaced* by the solver *still with pid $$*. Note that # ~/dev/def/smalltools-private/doc/examples/shell/bash-dollar-dollar-issue # documents this behaviour. exec $SOLVER --length=$WORD_LENGTH --output=/dev/null } main "$@"