Skip to content

Commit

Permalink
Fix termux-notification and termux-toast from hanging when running fr…
Browse files Browse the repository at this point in the history
…om background

In termux-notification, if content is not passed as an argument and then using `[ -t 0 ]` to detect if stdin is also not available does not work for background commands since that is only meant to check if stdin is open and is associated with a terminal. When running in background, the check will fail, since stdin will not be attached to a terminal, but moreover, no data will be available on stdin either and termux-api will hang forever waiting for input.

In termux-toast, similar thing happens where if toast text argument is not passed, it is assumed that data will be available on stdin, which it may not be, making termux-api hang forever waiting for input.

Now for both, if arguments are not passed, then stdin is attempted to be read directly with a 3s timeout without relying on termux-api. If no data is available on stdin, then an empty string will be passed, after aborting after 3s.

Fixes termux/termux-app#1925

Thanks to @krystean for investigating the issue.
  • Loading branch information
agnostic-apollo committed Apr 17, 2021
1 parent e5860f4 commit ff09377
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 93 deletions.
160 changes: 86 additions & 74 deletions scripts/termux-notification
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,79 @@ set -e -u -f

SCRIPTNAME=termux-notification
show_usage () {
echo "Usage: termux-notification [options]"
echo "Display a system notification. Content text is specified using -c/--content or read from stdin."
echo "Please read --help-actions for help with action arguments."
echo " --action action action to execute when pressing the notification"
echo " --alert-once do not alert when the notification is edited"
echo " --button1 text text to show on the first notification button"
echo " --button1-action action action to execute on the first notification button"
echo " --button2 text text to show on the second notification button"
echo " --button2-action action action to execute on the second notification button"
echo " --button3 text text to show on the third notification button"
echo " --button3-action action action to execute on the third notification button"
echo " -c/--content content content to show in the notification. Will take precedence over stdin."
echo " --group group notification group (notifications with the same group are shown together)"
echo " -h/--help show this help"
echo " --help-actions show the help for actions"
echo " -i/--id id notification id (will overwrite any previous notification with the same id)"
echo " --icon icon-name set the icon that shows up in the status bar. View available icons at"
echo " https://material.io/resources/icons/ (default icon: event_note)"
echo " --image-path path absolute path to an image which will be shown in the notification"
echo " --led-color rrggbb color of the blinking led as RRGGBB (default: none)"
echo " --led-off milliseconds number of milliseconds for the LED to be off while it's flashing (default: 800)"
echo " --led-on milliseconds number of milliseconds for the LED to be on while it's flashing (default: 800)"
echo " --on-delete action action to execute when the the notification is cleared"
echo " --ongoing pin the notification"
echo " --priority prio notification priority (high/low/max/min/default)"
echo " --sound play a sound with the notification"
echo " -t/--title title notification title to show"
echo " --vibrate pattern vibrate pattern, comma separated as in 500,1000,200"
echo " --type type notification style to use (default/media)"
echo "Media actions (available with --type \"media\"):"
echo " --media-next action to execute on the media-next button"
echo " --media-pause action to execute on the media-pause button"
echo " --media-play action to execute on the media-play button"
echo " --media-previous action to execute on the media-previous button"
exit 0
echo "Usage: termux-notification [options]"
echo "Display a system notification. Content text is specified using -c/--content or read from stdin."
echo "Please read --help-actions for help with action arguments."
echo " --action action action to execute when pressing the notification"
echo " --alert-once do not alert when the notification is edited"
echo " --button1 text text to show on the first notification button"
echo " --button1-action action action to execute on the first notification button"
echo " --button2 text text to show on the second notification button"
echo " --button2-action action action to execute on the second notification button"
echo " --button3 text text to show on the third notification button"
echo " --button3-action action action to execute on the third notification button"
echo " -c/--content content content to show in the notification. Will take"
echo " precedence over stdin. If content is not passed as"
echo " an argument or with stdin, then there will be a 3s delay."
echo " --group group notification group (notifications with the same"
echo " group are shown together)"
echo " -h/--help show this help"
echo " --help-actions show the help for actions"
echo " -i/--id id notification id (will overwrite any previous notification"
echo " with the same id)"
echo " --icon icon-name set the icon that shows up in the status bar. View"
echo " available icons at https://material.io/resources/icons/"
echo " (default icon: event_note)"
echo " --image-path path absolute path to an image which will be shown in the"
echo " notification"
echo " --led-color rrggbb color of the blinking led as RRGGBB (default: none)"
echo " --led-off milliseconds number of milliseconds for the LED to be off while"
echo " it's flashing (default: 800)"
echo " --led-on milliseconds number of milliseconds for the LED to be on while"
echo " it's flashing (default: 800)"
echo " --on-delete action action to execute when the the notification is cleared"
echo " --ongoing pin the notification"
echo " --priority prio notification priority (high/low/max/min/default)"
echo " --sound play a sound with the notification"
echo " -t/--title title notification title to show"
echo " --vibrate pattern vibrate pattern, comma separated as in 500,1000,200"
echo " --type type notification style to use (default/media)"
echo "Media actions (available with --type \"media\"):"
echo " --media-next action to execute on the media-next button"
echo " --media-pause action to execute on the media-pause button"
echo " --media-play action to execute on the media-play button"
echo " --media-previous action to execute on the media-previous button"
exit 0
}

show_help_actions () {
echo "This help refers to the arguments to options like --action, --on-delete, --button-1-action and --media-next."
echo
echo "All these commands take an action string as their argument, which is fed to \`dash -c\`."
echo "A few important things must be kept in mind when using actions:"
echo
echo "You should use actions that do things outside of the terminal, like --action \"termux-toast hello\"."
echo "Anything that outputs to the terminal is useless, so the output should either be redirected (--action \"ls > ~/ls.txt\") or shown to the user in a different way (--action \"ls|termux-toast\")."
echo
echo "Running more than one command in a single action is as easy as"
echo "--action \"command1; command2; command3\""
echo "or"
echo "--action \"if [ -e file ]; then termux-toast yes; else termux-toast no; fi\"."
echo
echo "For anything more complex, you should put your script in a file, make it executable, and use that as the action:"
echo "--action ~/bin/script"
echo
echo "The action is run in a different environment (not a subshell). Thus your environment is lost (most notably \$PATH), and ~/.profile is not sourced either. So if you need your \$PATH you should either:"
echo " - if the action is a script, set it explicitly in the script (e.g. export PATH=\"\$HOME/bin:\$PATH\")"
echo " - or use something like --action \"bash -l -c 'command1; command2'\")."
echo
echo "On Android N or above, you can use the special variable \$REPLY in your actions to use Android's Direct Reply feature."
echo "This prompts the user to enter some text directly in the notification, which is then substituted into your action."
echo " - termux-notification --button1 \"Answer\" --button1-action \"termux-toast \\\$REPLY\""
echo "will call the action:"
echo " - termux-toast \"Some text entered by the user\""
echo "Be careful to escape shell commands correctly for single or double quotes, e.g."
echo " --button1-action 'something \$REPLY' or --button1-action \"something \\\$REPLY\""
echo "This help refers to the arguments to options like --action, --on-delete, --button-1-action and --media-next."
echo
echo "All these commands take an action string as their argument, which is fed to \`dash -c\`."
echo "A few important things must be kept in mind when using actions:"
echo
echo "You should use actions that do things outside of the terminal, like --action \"termux-toast hello\"."
echo "Anything that outputs to the terminal is useless, so the output should either be redirected (--action \"ls > ~/ls.txt\") or shown to the user in a different way (--action \"ls|termux-toast\")."
echo
echo "Running more than one command in a single action is as easy as"
echo "--action \"command1; command2; command3\""
echo "or"
echo "--action \"if [ -e file ]; then termux-toast yes; else termux-toast no; fi\"."
echo
echo "For anything more complex, you should put your script in a file, make it executable, and use that as the action:"
echo "--action ~/bin/script"
echo
echo "The action is run in a different environment (not a subshell). Thus your environment is lost (most notably \$PATH), and ~/.profile is not sourced either. So if you need your \$PATH you should either:"
echo " - if the action is a script, set it explicitly in the script (e.g. export PATH=\"\$HOME/bin:\$PATH\")"
echo " - or use something like --action \"bash -l -c 'command1; command2'\")."
echo
echo "On Android N or above, you can use the special variable \$REPLY in your actions to use Android's Direct Reply feature."
echo "This prompts the user to enter some text directly in the notification, which is then substituted into your action."
echo " - termux-notification --button1 \"Answer\" --button1-action \"termux-toast \\\$REPLY\""
echo "will call the action:"
echo " - termux-toast \"Some text entered by the user\""
echo "Be careful to escape shell commands correctly for single or double quotes, e.g."
echo " --button1-action 'something \$REPLY' or --button1-action \"something \\\$REPLY\""
}

OPT_ACTION=""
Expand All @@ -79,6 +87,7 @@ OPT_BUTTON2_TEXT=""
OPT_BUTTON3_ACTION=""
OPT_BUTTON3_TEXT=""
OPT_CONTENT=""
OPT_CONTENT_PASSED=""
OPT_GROUP=""
OPT_ID=""
OPT_ICON=""
Expand All @@ -99,9 +108,9 @@ OPT_TYPE=""
OPT_VIBRATE=""

TEMP=`getopt \
-n $SCRIPTNAME \
-o hc:i:t: \
--long action:,alert-once,\
-n $SCRIPTNAME \
-o hc:i:t: \
--long action:,alert-once,\
button1:,button1-action:,\
button2:,button2-action:,\
button3:,button3-action:,\
Expand All @@ -112,8 +121,8 @@ media-previous:,media-next:,media-play:,media-pause:,\
on-delete:,ongoing,\
priority:,\
sound,title:,type:,vibrate: \
-s bash \
-- "$@"`
-s bash \
-- "$@"`
eval set -- "$TEMP"

while true; do
Expand All @@ -126,7 +135,7 @@ while true; do
--button2-action) OPT_BUTTON2_ACTION="$2"; shift 2;;
--button3) OPT_BUTTON3_TEXT="$2"; shift 2;;
--button3-action) OPT_BUTTON3_ACTION="$2"; shift 2;;
-c | --content) OPT_CONTENT="$2"; shift 2;;
-c | --content) OPT_CONTENT_PASSED=1; OPT_CONTENT="$2"; shift 2;;
--group) OPT_GROUP="$2"; shift 2;;
-h | --help) show_usage;;
--help-actions) show_help_actions; exit 0;;
Expand Down Expand Up @@ -187,10 +196,13 @@ if [ -n "$OPT_TITLE" ]; then set -- "$@" --es title "$OPT_TITLE"; fi
if [ -n "$OPT_TYPE" ]; then set -- "$@" --es type "$OPT_TYPE"; fi
if [ -n "$OPT_VIBRATE" ]; then set -- "$@" --ela vibrate "$OPT_VIBRATE"; fi

if [ -n "$OPT_CONTENT" ] || [ -t 0 ]; then
# we either have some content, so it takes precedence over STDIN
# or have no STDIN, so we must use content even if empty
echo "$OPT_CONTENT" | @TERMUX_PREFIX@/libexec/termux-api Notification "$@"
else # use STDIN
@TERMUX_PREFIX@/libexec/termux-api Notification "$@"
# If content was not passed as an argument, then attempt to read from STDIN with a 3s timeout
# Content argumOPT_CONTENT="$(echo "$OPT_CONTENT")" # trim trailing newlinesent takes precedence over STDIN
if [[ "$OPT_CONTENT_PASSED" != "1" ]]; then
set +e; IFS= read -t 3 -r -d '' OPT_CONTENT; set -e;
fi

# Trim trailing newlines
OPT_CONTENT="$(echo "$OPT_CONTENT")"

echo "$OPT_CONTENT" | @TERMUX_PREFIX@/libexec/termux-api Notification "$@"
49 changes: 30 additions & 19 deletions scripts/termux-toast
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,46 @@ set -e -u

SCRIPTNAME=termux-toast
show_usage () {
echo "Usage: termux-toast [-b bgcolor] [-c color] [-g gravity] [-s] [text]"
echo "Show text in a Toast (a transient popup). The text to show is either supplied as arguments or read from stdin if no arguments are given."
echo " -h show this help"
echo " -b set background color (default: gray)"
echo " -c set text color (default: white)"
echo " -g set position of toast: [top, middle, or bottom] (default: middle)"
echo " -s only show the toast for a short while"
echo "NOTE: color can be a standard name (i.e. red) or 6 / 8 digit hex value (i.e. \"#FF0000\" or \"#FFFF0000\") where order is (AA)RRGGBB. Invalid color will revert to default value"
exit 0
echo "Usage: termux-toast [-b bgcolor] [-c color] [-g gravity] [-s] [text]"
echo "Show text in a Toast (a transient popup)."
echo "The toast text is either supplied as arguments or read from stdin"
echo "if no arguments are given. Arguments will take precedence over stdin."
echo "If toast text is not passed as arguments or with stdin, then there will"
echo "be a 3s delay."
echo " -h show this help"
echo " -b set background color (default: gray)"
echo " -c set text color (default: white)"
echo " -g set position of toast: [top, middle, or bottom] (default: middle)"
echo " -s only show the toast for a short while"
echo "NOTE: color can be a standard name (i.e. red) or 6 / 8 digit hex value (i.e. \"#FF0000\" or \"#FFFF0000\") where order is (AA)RRGGBB. Invalid color will revert to default value"
exit 0
}

PARAMS=""
while getopts :hsc:b:g: option
do
case "$option" in
h) show_usage;;
s) PARAMS+=" --ez short true";;
c) PARAMS+=" --es text_color $OPTARG";;
b) PARAMS+=" --es background $OPTARG";;
g) PARAMS+=" --es gravity $OPTARG";;
?) echo "$SCRIPTNAME: illegal option -$OPTARG"; exit 1;
esac
case "$option" in
h) show_usage;;
s) PARAMS+=" --ez short true";;
c) PARAMS+=" --es text_color $OPTARG";;
b) PARAMS+=" --es background $OPTARG";;
g) PARAMS+=" --es gravity $OPTARG";;
?) echo "$SCRIPTNAME: illegal option -$OPTARG"; exit 1;
esac
done
shift $((OPTIND-1))

CMD="@TERMUX_PREFIX@/libexec/termux-api Toast $PARAMS"

# If toast text was not passed as an argument, then attempt to read from STDIN with a 3s timeout
# Toast text arguments takes precedence over STDIN
if [ $# = 0 ]; then
$CMD
set +e; IFS= read -t 3 -r -d '' TOAST_TEXT; set -e;
else
echo "$@" | $CMD
TOAST_TEXT="$*"
fi

# Trim trailing newlines
TOAST_TEXT="$(echo "$TOAST_TEXT")"

echo "$TOAST_TEXT" | $CMD

0 comments on commit ff09377

Please sign in to comment.