marmistrz
marmistrz

Reputation: 6394

Bash FIFOs not working as expected - how to debug it

I was given the following assignment. We have a prebuilt GUI in a binary form, kept in $GAME_BIN. I have to write a script which connects the GUI with the AI engine. This is my code, which is pretty self descriptive. We have in this case: GAME_BIN=./sredniowiecze_gui, ai1=./idle_ai.sh, ai2=./idle_ai.sh

#!/bin/bash

# arguments parsing and setting $args - not posting this here

gui_outpipe=$(mktemp -u)
gui_inpipe=$(mktemp -u)
ai1_outpipe=$(mktemp -u)
ai1_inpipe=$(mktemp -u)
ai2_outpipe=$(mktemp -u)
ai2_inpipe=$(mktemp -u)
mkfifo $gui_outpipe $gui_inpipe $ai1_outpipe $ai1_inpipe $ai2_inpipe $ai2_outpipe

printinit 1 > $ai1_inpipe &
printinit 2 > $ai2_inpipe &
$GAME_BIN $args < $gui_inpipe &
$ai1 < $ai1_inpipe > $ai1_outpipe &
$ai1 < $ai2_inpipe > $ai2_outpipe &
while true; do
    echo "Started the loop"
    while true; do
        read line < $ai1_outpipe || echo "Nothing read"
        echo $line
        if [[ $line ]]; then
            echo "$line" > $gui_inpipe
            echo "$line" > $ai2_inpipe
            if [[ "$line" == "END_TURN" ]]; then
                break
            fi
        fi
    done
    sleep $turndelay
    while true; do
        read line < $ai2_outpipe || echo "nothing read"
        echo $line
        if [[ $line ]]; then
            echo "$line" > $gui_inpipe
            echo "$line" > $ai2_inpipe
            if [[ "$line" == "END_TURN" ]]; then
                break
            fi
        fi
    done
    sleep $turndelay
done

wait

I created a simple idle AI contained in idle_ai.sh

#!/bin/sh
while true; do
    echo END_TURN
done

Then the END_TURN message from the GUI is not received at all. On the other hand, the second END_TURN in line (*) is not received by the script. If I use my own C-written AI - very long code, not posting it here, no information from the AI is received in the second run of the while loop

I have absolutely no idea how to debug it.

Since I'm not eager to run binaries unsandboxed, I'm calling the script by firejail ./game.sh [irrelevant parameters]

EDIT after adding set -x the output is

INIT 10 3 1 1 1 3 9
+ [[ -n ./idle_ai.sh ]]
+ [[ -n '' ]]
+ [[ -n ./idle_ai.sh ]]
+ printinit 1
+ ./sredniowiecze_gui -human2
+ true
+ echo 'Started the loop'
Started the loop
+ true
+ read line
+ ./idle_ai.sh
+ echo 'INIT 10 3 1 1 1 3 9'
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ true
+ read line
+ echo MOVE 5 9 5 8
MOVE 5 9 5 8
+ [[ -n MOVE 5 9 5 8 ]]
+ echo 'MOVE 5 9 5 8'

In AI vs AI mode:

INIT 10 3 1 1 1 3 9
+ [[ -n ./idle_ai.sh ]]
+ [[ -n ./idle_ai.sh ]]
+ printinit 1
+ printinit 2
+ ./sredniowiecze_gui
+ ./idle_ai.sh
+ true
+ echo 'Started the loop'
Started the loop
+ echo 'INIT 10 3 1 1 1 3 9'
+ true
+ read line
+ ./idle_ai.sh
+ echo 'INIT 10 3 2 1 1 3 9'
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ sleep 1
+ true
+ read line
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ sleep 1
+ true
+ echo 'Started the loop'
Started the loop
+ true
+ read line

EDIT2 I did the suggested changes, now my code is:

printinit 1 > $ai1_inpipe &
printinit 2 > $ai2_inpipe &
$GAME_BIN $args < $gui_inpipe &
$ai1 < $ai1_inpipe > $ai1_outpipe &
echo $!
$ai2 < $ai2_inpipe > $ai2_outpipe &
echo $!
while true; do
    echo "Started the loop"
    while true; do
        read -u3 line || echo "Nothing read"
        echo $line
        if [[ $line ]]; then
            echo "$line" > $gui_inpipe
            echo "$line" > $ai2_inpipe
            if [[ "$line" == "END_TURN" ]]; then
                break
            fi
        fi
    done
    sleep $turndelay
    while true; do
        read -u4 line || echo "nothing read"
        echo $line
        if [[ $line ]]; then
            echo "$line" > $gui_inpipe
            echo "$line" > $ai1_inpipe
            if [[ "$line" == "END_TURN" ]]; then
                break
            fi
        fi
    done
    sleep $turndelay
done 3<$ai1_outpipe 4<$ai2_outpipe

And now the script gets stuck on the echo "$line" > $ai1_inpipe line, although the $ai2 process is still running.

EDIT3. Now the log with set -x is

INIT 10 3 1 1 1 3 9
+ [[ -n ./idle_ai.sh ]]
+ [[ -n ./idle_ai.sh ]]
+ printinit 1
+ printinit 2
+ ./sredniowiecze_gui
+ echo 26
26
+ ./idle_ai.sh
+ echo 'INIT 10 3 1 1 1 3 9'
+ echo 27
27
+ ./idle_ai.sh
+ echo 'INIT 10 3 2 1 1 3 9'
+ true
+ echo 'Started the loop'
Started the loop
+ true
+ read -u3 line
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN
+ [[ END_TURN == \E\N\D\_\T\U\R\N ]]
+ break
+ sleep 1
+ true
+ read -u4 line
+ echo END_TURN
END_TURN
+ [[ -n END_TURN ]]
+ echo END_TURN
+ echo END_TURN

If you add an echo FOO before and after the call, like this:

            echo FOO
            echo "$line" > $ai1_inpipe
            echo BAR

then echo FOO is executed and echo BAR not.

Upvotes: 0

Views: 70

Answers (1)

Camusensei
Camusensei

Reputation: 1553

You're using read < input, which sucks all the input and only uses the first line. Instead of doing that, you should have read read from open file descriptors, like this:

EDIT: Same thing with writing to the fifo files with echo

while true; do
echo "Started the loop"
while true; do
    read -u3 line || echo "Nothing read"
    ...
        echo "$line" >&5
        echo "$line" >&6
    ...
done
sleep $turndelay
while true; do
    read -u4 line || echo "nothing read"
    ...
        echo "$line" >&5
        echo "$line" >&7
    ...
sleep $turndelay
done 3<$ai1_outpipe 4<$ai2_outpipe 5>$gui_inpipe 6>$ai2_inpipe 7>$ai1_inpipe

See these links for more help on the topic:

Upvotes: 1

Related Questions