Hello. After two years of using pass and following the mailing list I've finally found a way to add multi-clip. This is a feature heavily inspired by Nicolas S. Dade's pwsafe. It started as an idea for a feature request, then it was a proof of concept, and now, after a few weeks of poking and prodding, I'm happy to share a full working alt version of passmenu. I use it every day and I hope you will too.
Thank you milki and Jason and others for your past correspondence helping to steer me in this direction. [rolling][loops] First: If you like the way passmenu currently behaves, then you wont notice much of a change. In this version, I've preserved all single clip behavior. This patch: If you add my patch, you will gain the ability to select multiple passwords and paste them in series. Meaning, after the first paste, passmenu continues and lines up another. This is useful for pasting logins and passwords. To do this, in dmenu press Ctrl-Enter instead of Enter to continue selecting entries. Hit Enter on the last entry. Now you can paste each entry once. The magic line: ## line #73 <code>xclip -l 1 -quiet -sel "$X_SELECTION" </code> Here xclip waits for one X selection request and prints. https://github.com/ampling/pass/blob/passmenu_multi-clip/contrib/dmenu/passmenu#L73 Multi-line vs Fist-line storage. Yes. I'm aware of both methods of storage. I had both in mind when writing this. I found it easier to address first-line storage in this patch, but I have an idea of how to add multi-line clips later. Pro-tip ## passmenu --type I'm really liking xdotool. Try setting a keyboard shortcut for passmenu --type, then select url, login and pass. Then, paste, paste, paste, enter. Xdotool gets arround places which mess with the clipboard. I'm looking at you firefox omnibar. Try it. It's simply the most constantly low hassle authentication process I've yet tried. What I've learned: It's a bad idea for a pass or passmenu process to overlap with another process of pass or passmenu. I needed a file lock for the clipboard. I like flock as a file lock, but it wasn't default on all platforms. With portability in mind, I went with mktemp -d. I've done my best to go with sane defaults here. If you feel there's a better way, please let me know. In addition my the diff bellow, you can find my patch with full history attached and on github. https://github.com/ampling/pass/blob/passmenu_multi-clip/contrib/dmenu/passmenu diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index 7a9c517..68b4150 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -1,25 +1,98 @@ #!/usr/bin/env bash -shopt -s nullglob globstar +shopt -s nullglob globstar failglob +set -euo pipefail +tool=${1:-} typeit=0 -if [[ $1 == "--type" ]]; then - typeit=1 - shift +if [[ $tool == "--type" ]]; then + typeit=1 + shift fi -prefix=${PASSWORD_STORE_DIR-~/.password-store} +X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}" +CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}" +prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}" + password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) +password=( ${password:-""} ) +before=( "${before:-}" ) +userID="$(id -u $(whoami))" +stalelock=( "${stalelock:-""}" ) +stalelock=( "$(find '/tmp' -maxdepth 1 -name "passmenulock."$userID".*" -user $(whoami) -print0 -quit)" ) && +cleanup=True || { true; cleanup=False; } + +_finish () { + [[ -n $before ]] && + printf "$before" | base64 -d | xclip -sel "$X_SELECTION" -i + [[ True = "$cleanup" ]] && + if compgen -G "/tmp/passmenulock.1000*" >/dev/null 2>&1 ;then + rmdir /tmp/passmenulock."$userID".* >/dev/null 2>&1 + fi + exit +} +trap _finish EXIT -password=$(printf '%s\n' "${password_files[@]}" | dmenu "$@") +## Clearing stale Xclip loops avoids a posible race condition. +if test -n "$stalelock" ;then + report=( "$(ps -u $(id -u $(whoami)) aux | grep "bash" | + grep "passmenu" | grep -v "$$")" ) + stalePID=( "$(printf $stalelock | + sed -e "s/\/tmp\/passmenulock\.[0-9]\{1,6\}\?\..*\.//g")" ) + if [[ "$report" == *"$stalePID"* ]] ;then + kill "$stalePID" || exit 1 + else + rmdir /tmp/passmenulock.$userID.* >/dev/null 2>&1 || exit 1 + fi +fi + +## dmenu exits on KeyPress not KeyRelease. +# It would be nice to send KeyRelease event to some dummy window. +# psydocode: xdotool getwindowfocus; create dummy window; +# exec dmenu; close dummy window; restore focus. +for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do + passel+=("$password") +done [[ -n $password ]] || exit +# It would be nice to first somehow test if string exists. +before="$(xclip -sel "$X_SELECTION" -o 2>/dev/null | base64)" || true + +umask 077 +( mktemp -d "/tmp/passmenulock."$userID".XXXXXXXXXX"."$$" >/dev/null 2>&1 && cleanup=True || + { echo >&2 ":: Unable to make a filelock."; exit 1; } ) if [[ $typeit -eq 0 ]]; then - pass show -c "$password" 2>/dev/null + if [ ${#passel[@]} -gt "1" ]; then + round=0 + for entry in "${passel[@]}"; do + printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" + pass show "$entry" | sed '1!d' | tr -d '\n' | + xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1 + round=`expr $round + 1` + done + else + printf '%s\n' "Sending "$password" via "$X_SELECTION"" + pass show "$password" | sed '1!d' | tr -d '\n' | + xclip -sel "$X_SELECTION" -i && sleep "$CLIP_TIME" + fi else - pass show "$password" | { read -r pass; printf %s "$pass"; } | - xdotool type --clearmodifiers --file - + command -v xdotool >/dev/null 2>&1 || + { echo >&2 "e: cannot find xdotool."; exit 1; } + if [ ${#passel[@]} -gt "1" ]; then + round=0 + for entry in "${passel[@]}"; do + printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" + printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1 + pass show "$entry" | sed '1!d' | tr -d '\n' | + xdotool type --clearmodifiers --file - + round=`expr $round + 1` + done + else + pass show "$password" | sed '1!d' | tr -d '\n' | + xdotool type --clearmodifiers --file - + fi fi +exit -- At your service, mitfree http://ampling.com
From 1d2e5d9d0b3e46e1ce72e4151193eb648d668176 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Fri, 8 Apr 2016 23:38:57 -0400 Subject: [PATCH 01/11] Adding xclip loop to select multiple pass entries. --- contrib/dmenu/passmenu | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index 7a9c517..b80399c 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -4,22 +4,27 @@ shopt -s nullglob globstar typeit=0 if [[ $1 == "--type" ]]; then - typeit=1 - shift + typeit=1 + shift fi +X_SELECTION="$PASSWORD_STORE_X_SELECTION" prefix=${PASSWORD_STORE_DIR-~/.password-store} + password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) -password=$(printf '%s\n' "${password_files[@]}" | dmenu "$@") +if [[ $typeit -eq 0 ]]; then + for password in $(printf '%s\n' "${password_files[@]}" | + dmenu -f "$@"); do + pass show $password | + xclip -l 1 -quiet -sel $X_SELECTION + done [[ -n $password ]] || exit -if [[ $typeit -eq 0 ]]; then - pass show -c "$password" 2>/dev/null else - pass show "$password" | { read -r pass; printf %s "$pass"; } | - xdotool type --clearmodifiers --file - + pass show "$password" | { read -r pass; printf %s "$pass"; } | + xdotool type --clearmodifiers --file - fi -- 2.8.0 From 4b003d9bbd9b79d71250aeee029468806bf16e09 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Sat, 9 Apr 2016 00:46:26 -0400 Subject: [PATCH 02/11] Add condition to skip xclip loop on 1 selection. --- contrib/dmenu/passmenu | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index b80399c..ddfb07f 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -17,13 +17,20 @@ password_files=( "${password_files[@]%.gpg}" ) if [[ $typeit -eq 0 ]]; then for password in $(printf '%s\n' "${password_files[@]}" | - dmenu -f "$@"); do - pass show $password | - xclip -l 1 -quiet -sel $X_SELECTION + dmenu -f "$@"); do + lot+=($password) done -[[ -n $password ]] || exit + [[ -n $password ]] || exit + if [ ${#lot[@]} -gt "0" ]; then + for entry in "${lot[@]}"; do + pass show $entry | awk 'NR==1' | + xclip -l 1 -quiet -sel $X_SELECTION + done + else + pass show -c "$password" 2>/dev/null + fi else pass show "$password" | { read -r pass; printf %s "$pass"; } | xdotool type --clearmodifiers --file - -- 2.8.0 From 1e52e9fc165961a94bc834d5fd58ee2669eaae1d Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Thu, 14 Apr 2016 02:03:17 -0400 Subject: [PATCH 03/11] Restores clipboard to previous state. --- contrib/dmenu/passmenu | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index ddfb07f..789d0de 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -15,22 +15,24 @@ password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) -if [[ $typeit -eq 0 ]]; then - for password in $(printf '%s\n' "${password_files[@]}" | - dmenu -f "$@"); do +for password in $(printf '%s\n' "${password_files[@]}" | + dmenu -f "$@"); do lot+=($password) - done +done - [[ -n $password ]] || exit +[[ -n $password ]] || exit - if [ ${#lot[@]} -gt "0" ]; then - for entry in "${lot[@]}"; do - pass show $entry | awk 'NR==1' | - xclip -l 1 -quiet -sel $X_SELECTION +if [[ $typeit -eq 0 ]]; then + xclip -sel clip -o | xclip -sel sec -i + if [ ${#lot[@]} -gt "1" ]; then + for entry in "${lot[@]}"; do + pass show $entry | awk 'NR==1' | + xclip -l 1 -quiet -sel $X_SELECTION &>/dev/null done + xclip -sel sec -o | xclip -sel clip -i else - pass show -c "$password" 2>/dev/null - fi + pass show -c "$password" &>/dev/null + fi else pass show "$password" | { read -r pass; printf %s "$pass"; } | xdotool type --clearmodifiers --file - -- 2.8.0 From 24d3194a119c4503d471d09159d11f3d4c12eda3 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Fri, 22 Apr 2016 14:44:51 -0400 Subject: [PATCH 04/11] Clears secondary clipboard after use. --- contrib/dmenu/passmenu | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index 789d0de..84b4ae4 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -8,13 +8,14 @@ if [[ $1 == "--type" ]]; then shift fi -X_SELECTION="$PASSWORD_STORE_X_SELECTION" -prefix=${PASSWORD_STORE_DIR-~/.password-store} +X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}" +prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}" password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) +## Press Ctrl-Return to continue selecting entries. for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do lot+=($password) @@ -23,15 +24,19 @@ done [[ -n $password ]] || exit if [[ $typeit -eq 0 ]]; then - xclip -sel clip -o | xclip -sel sec -i + xclip -sel "$X_SELECTION" -o | xclip -sel sec -i if [ ${#lot[@]} -gt "1" ]; then + x=0 for entry in "${lot[@]}"; do - pass show $entry | awk 'NR==1' | - xclip -l 1 -quiet -sel $X_SELECTION &>/dev/null + printf '%s\n' "Sending "${lot[$x]}" via "$X_SELECTION"" + pass show $entry | head -n 1 | tr -d '\n' | + xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null + x=`expr $x + 1` done - xclip -sel sec -o | xclip -sel clip -i + xclip -sel sec -o | xclip -sel "$X_SELECTION" -i + printf "" | xclip -sel sec -i else - pass show -c "$password" &>/dev/null + pass -c "$password" 2>/dev/null fi else pass show "$password" | { read -r pass; printf %s "$pass"; } | -- 2.8.0 From 8e25eeeab68810ec295fb5d8cb2b4412bcd529f2 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Sat, 23 Apr 2016 02:36:55 -0400 Subject: [PATCH 05/11] Limits use of secondary clip to multi selections. --- contrib/dmenu/passmenu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index 84b4ae4..a784481 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -24,17 +24,17 @@ done [[ -n $password ]] || exit if [[ $typeit -eq 0 ]]; then - xclip -sel "$X_SELECTION" -o | xclip -sel sec -i if [ ${#lot[@]} -gt "1" ]; then x=0 + xclip -sel "$X_SELECTION" -o | xclip -sel sec -i for entry in "${lot[@]}"; do printf '%s\n' "Sending "${lot[$x]}" via "$X_SELECTION"" pass show $entry | head -n 1 | tr -d '\n' | xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null x=`expr $x + 1` done - xclip -sel sec -o | xclip -sel "$X_SELECTION" -i - printf "" | xclip -sel sec -i + xclip -sel sec -o | xclip -sel "$X_SELECTION" -i + printf "" | xclip -sel sec -i else pass -c "$password" 2>/dev/null fi -- 2.8.0 From 20d3a8a8c55dfb31fa9cdc191b156f7d98ac3a53 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Sat, 23 Apr 2016 17:12:29 -0400 Subject: [PATCH 06/11] Changes variables and comments for readability. --- contrib/dmenu/passmenu | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index a784481..45eb66f 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -8,30 +8,31 @@ if [[ $1 == "--type" ]]; then shift fi -X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}" +X_SELECTION="${PASSWORD_STORE_X_SELECTION:-primary}" prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}" password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) -## Press Ctrl-Return to continue selecting entries. +## dmenu exits on KeyPress. +## We should send KeyRelease event to some dummy window. for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do - lot+=($password) + passel+=($password) done [[ -n $password ]] || exit if [[ $typeit -eq 0 ]]; then - if [ ${#lot[@]} -gt "1" ]; then - x=0 + if [ ${#passel[@]} -gt "1" ]; then + round=0 xclip -sel "$X_SELECTION" -o | xclip -sel sec -i - for entry in "${lot[@]}"; do - printf '%s\n' "Sending "${lot[$x]}" via "$X_SELECTION"" + for entry in "${passel[@]}"; do + printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" pass show $entry | head -n 1 | tr -d '\n' | xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null - x=`expr $x + 1` + round=`expr $round + 1` done xclip -sel sec -o | xclip -sel "$X_SELECTION" -i printf "" | xclip -sel sec -i -- 2.8.0 From 499a2b10739514860744c0179f00a551bd34cd12 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Sat, 23 Apr 2016 20:22:08 -0400 Subject: [PATCH 07/11] Adding multi password selection to option --type. --- contrib/dmenu/passmenu | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index 45eb66f..1b0339f 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -40,6 +40,20 @@ if [[ $typeit -eq 0 ]]; then pass -c "$password" 2>/dev/null fi else - pass show "$password" | { read -r pass; printf %s "$pass"; } | - xdotool type --clearmodifiers --file - + if [ ${#passel[@]} -gt "1" ]; then + round=0 + xclip -sel "$X_SELECTION" -o | xclip -sel sec -i + for entry in "${passel[@]}"; do + printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" + printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null + pass show "$entry" | { read -r pass; printf %s "$pass"; } | + xdotool type --clearmodifiers --file - + round=`expr $round + 1` + done + xclip -sel sec -o | xclip -sel "$X_SELECTION" -i + printf "" | xclip -sel sec -i + else + pass show "$password" | { read -r pass; printf %s "$pass"; } | + xdotool type --clearmodifiers --file - + fi fi -- 2.8.0 From 67dacee2237bf12dfa0f6e14806971ad2daab7ea Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Wed, 27 Apr 2016 20:29:07 -0400 Subject: [PATCH 08/11] Locks script and attemps to clear old loop. --- contrib/dmenu/passmenu | 91 ++++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index 1b0339f..e842f13 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -4,56 +4,69 @@ shopt -s nullglob globstar typeit=0 if [[ $1 == "--type" ]]; then - typeit=1 - shift + typeit=1 + shift fi -X_SELECTION="${PASSWORD_STORE_X_SELECTION:-primary}" +X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}" prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}" password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) -## dmenu exits on KeyPress. -## We should send KeyRelease event to some dummy window. -for password in $(printf '%s\n' "${password_files[@]}" | - dmenu -f "$@"); do - passel+=($password) +## Preexisting xclip loops can cause unanticipated behavior. +exec {lock_fd}>/tmp/passmenulock || exit 1 +flock -n "$lock_fd" || + { xclip -sel "$X_SELECTION" -o >/dev/null 2>&1 + flock -n "$lock_fd" || + printf '%s\n' 'Already running' >&2 + exit 1 + } + +## dmenu exits on KeyPress not KeyRelease. +## It would be nice to send KeyRelease event to some dummy window. +for password in $(printf '%s\n' "${password_files[@]}" | + dmenu -f "$@"); do + passel+=($password) done [[ -n $password ]] || exit - + if [[ $typeit -eq 0 ]]; then - if [ ${#passel[@]} -gt "1" ]; then - round=0 - xclip -sel "$X_SELECTION" -o | xclip -sel sec -i - for entry in "${passel[@]}"; do - printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" - pass show $entry | head -n 1 | tr -d '\n' | - xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null - round=`expr $round + 1` - done - xclip -sel sec -o | xclip -sel "$X_SELECTION" -i - printf "" | xclip -sel sec -i - else - pass -c "$password" 2>/dev/null - fi + if [ ${#passel[@]} -gt "1" ]; then + xclip -sel "$X_SELECTION" -o | xclip -sel sec -i + round=0 + for entry in "${passel[@]}"; do + printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" + pass show $entry | sed '1!d' | tr -d '\n' | + xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1 + ~/bin/beeponce + round=`expr $round + 1` + done + xclip -sel sec -o | xclip -sel "$X_SELECTION" -i + else + pass -c "$password" 2>/dev/null + fi else - if [ ${#passel[@]} -gt "1" ]; then - round=0 - xclip -sel "$X_SELECTION" -o | xclip -sel sec -i - for entry in "${passel[@]}"; do - printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" - printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" &>/dev/null - pass show "$entry" | { read -r pass; printf %s "$pass"; } | - xdotool type --clearmodifiers --file - - round=`expr $round + 1` - done - xclip -sel sec -o | xclip -sel "$X_SELECTION" -i - printf "" | xclip -sel sec -i - else - pass show "$password" | { read -r pass; printf %s "$pass"; } | - xdotool type --clearmodifiers --file - - fi + command -v xdotool >/dev/null 2>&1 || + { echo >&2 "e: cannot find xdotool."; exit 1; } + if [ ${#passel[@]} -gt "1" ]; then + xclip -sel "$X_SELECTION" -o | xclip -sel sec -i + round=0 + for entry in "${passel[@]}"; do + printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" + printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1 + ~/bin/beeponce + pass show "$entry" | sed '1!d' | tr -d '\n' | + xdotool type --clearmodifiers --file - + round=`expr $round + 1` + done + xclip -sel sec -o | xclip -sel "$X_SELECTION" -i + else + pass show "$password" | sed '1!d' | tr -d '\n' | + xdotool type --clearmodifiers --file - + fi fi +printf "" | xclip -sel sec -i +flock -u "$lock_fd" -- 2.8.0 From 5cee86eb671b0db6229bcb490ca1762ae2eb8d76 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Sun, 1 May 2016 17:49:36 -0400 Subject: [PATCH 09/11] A portable process lock avoids unanticipated behavior. --- contrib/dmenu/passmenu | 68 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index e842f13..021c48f 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -1,6 +1,7 @@ #!/usr/bin/env bash shopt -s nullglob globstar +set -o errexit typeit=0 if [[ $1 == "--type" ]]; then @@ -9,64 +10,93 @@ if [[ $1 == "--type" ]]; then fi X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}" +CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}" prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}" password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) -## Preexisting xclip loops can cause unanticipated behavior. -exec {lock_fd}>/tmp/passmenulock || exit 1 -flock -n "$lock_fd" || - { xclip -sel "$X_SELECTION" -o >/dev/null 2>&1 - flock -n "$lock_fd" || - printf '%s\n' 'Already running' >&2 - exit 1 +_finish () { + [[ 1 = "$remove" ]] + rmdir /tmp/passmenu."$uid".* 2>/dev/null + ## kill more child processes here. + # echo $(ps -o pgid=$$ | grep -o [0-9]*) + printf "$buffer" | xclip -sel "$X_SELECTION" -i + exit } +trap _finish EXIT + +_fire () { + stale="$(find /tmp/passmenu."$uid".* -print -quit | + sed -e "s/\/tmp\/passmenu\.[0-9]\{1,6\}\?\..*\.//g")" + report="$(ps -u $(id -u $(whoami)) aux | grep "bash" | + grep "passmenu" | grep -v "$$")" + if [[ "$report" == *"passmenu"* ]] ;then + kill "$stale" && rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1 + else + rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1 + fi +} + +## Clearing stale Xclip loops avoids a posible race condition. +uid="$(id -u $(whoami))" +remove=0 +if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then + question=$(printf 'yes\nno'| + dmenu -nf red -f -p "The clipboard is locked by another process. Kill it with fire?") + if [ "$question" = 'yes' ] ;then + _fire + else + exit 0 + fi +fi ## dmenu exits on KeyPress not KeyRelease. ## It would be nice to send KeyRelease event to some dummy window. +## psydocode: xdotool getwindowfocus; create dummy window; +## exec dmenu; close dummy window; restore focus. for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do - passel+=($password) + passel+=("$password") done [[ -n $password ]] || exit - + +umask 077 +mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 || exit 1 +remove=1 + +buffer="$(xclip -sel "$X_SELECTION" -o)" if [[ $typeit -eq 0 ]]; then if [ ${#passel[@]} -gt "1" ]; then - xclip -sel "$X_SELECTION" -o | xclip -sel sec -i round=0 for entry in "${passel[@]}"; do printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" - pass show $entry | sed '1!d' | tr -d '\n' | + pass show "$entry" | sed '1!d' | tr -d '\n' | xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1 - ~/bin/beeponce round=`expr $round + 1` done - xclip -sel sec -o | xclip -sel "$X_SELECTION" -i else - pass -c "$password" 2>/dev/null + pass show "$password" | sed '1!d' | tr -d '\n' | + xclip -sel "$X_SELECTION" -i + sleep "$CLIP_TIME" fi else command -v xdotool >/dev/null 2>&1 || { echo >&2 "e: cannot find xdotool."; exit 1; } if [ ${#passel[@]} -gt "1" ]; then - xclip -sel "$X_SELECTION" -o | xclip -sel sec -i round=0 for entry in "${passel[@]}"; do printf '%s\n' "Sending "${passel[$round]}" via "$X_SELECTION"" printf '' | xclip -l 1 -quiet -sel "$X_SELECTION" >/dev/null 2>&1 - ~/bin/beeponce pass show "$entry" | sed '1!d' | tr -d '\n' | xdotool type --clearmodifiers --file - round=`expr $round + 1` done - xclip -sel sec -o | xclip -sel "$X_SELECTION" -i else pass show "$password" | sed '1!d' | tr -d '\n' | xdotool type --clearmodifiers --file - fi fi -printf "" | xclip -sel sec -i -flock -u "$lock_fd" +exit -- 2.8.0 From f8b10ed59edbb293e3728f29fc087ddcf316cc3c Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Fri, 6 May 2016 11:58:34 -0400 Subject: [PATCH 10/11] Adding strict mode. --- contrib/dmenu/passmenu | 58 ++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index 021c48f..e6e2cfc 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -1,10 +1,11 @@ #!/usr/bin/env bash -shopt -s nullglob globstar -set -o errexit +shopt -s nullglob globstar failglob +set -euo pipefail +tool=${1:-} typeit=0 -if [[ $1 == "--type" ]]; then +if [[ $tool == "--type" ]]; then typeit=1 shift fi @@ -18,56 +19,49 @@ password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) _finish () { - [[ 1 = "$remove" ]] - rmdir /tmp/passmenu."$uid".* 2>/dev/null - ## kill more child processes here. - # echo $(ps -o pgid=$$ | grep -o [0-9]*) - printf "$buffer" | xclip -sel "$X_SELECTION" -i + [[ -n $before ]] && + printf "$before" | xclip -sel "$X_SELECTION" -i + [[ True = "$remove" ]] && + if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then + rmdir /tmp/passmenu."$uid".* >/dev/null 2>&1 + fi exit - } +} trap _finish EXIT -_fire () { +## Clearing stale Xclip loops avoids a posible race condition. +uid="$(id -u $(whoami))" +remove=False +if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then stale="$(find /tmp/passmenu."$uid".* -print -quit | sed -e "s/\/tmp\/passmenu\.[0-9]\{1,6\}\?\..*\.//g")" report="$(ps -u $(id -u $(whoami)) aux | grep "bash" | grep "passmenu" | grep -v "$$")" - if [[ "$report" == *"passmenu"* ]] ;then + if [[ "$report" == *"$stale"* ]] ;then kill "$stale" && rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1 else rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1 fi -} - -## Clearing stale Xclip loops avoids a posible race condition. -uid="$(id -u $(whoami))" -remove=0 -if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then - question=$(printf 'yes\nno'| - dmenu -nf red -f -p "The clipboard is locked by another process. Kill it with fire?") - if [ "$question" = 'yes' ] ;then - _fire - else - exit 0 - fi fi ## dmenu exits on KeyPress not KeyRelease. ## It would be nice to send KeyRelease event to some dummy window. ## psydocode: xdotool getwindowfocus; create dummy window; ## exec dmenu; close dummy window; restore focus. -for password in $(printf '%s\n' "${password_files[@]}" | - dmenu -f "$@"); do +password=${password:-} +for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do passel+=("$password") done +before=${before:-} [[ -n $password ]] || exit +before="$(xclip -sel "$X_SELECTION" -o >/dev/null 2>&1)" || true umask 077 -mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 || exit 1 -remove=1 +mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 || +{ echo >&2 ":: Unable to make a filelock."; exit 1; } +remove=True -buffer="$(xclip -sel "$X_SELECTION" -o)" if [[ $typeit -eq 0 ]]; then if [ ${#passel[@]} -gt "1" ]; then round=0 @@ -78,12 +72,12 @@ if [[ $typeit -eq 0 ]]; then round=`expr $round + 1` done else + printf '%s\n' "Sending "$password" via "$X_SELECTION"" pass show "$password" | sed '1!d' | tr -d '\n' | - xclip -sel "$X_SELECTION" -i - sleep "$CLIP_TIME" + xclip -sel "$X_SELECTION" -i && sleep "$CLIP_TIME" fi else - command -v xdotool >/dev/null 2>&1 || + command -v xdotool >/dev/null 2>&1 || { echo >&2 "e: cannot find xdotool."; exit 1; } if [ ${#passel[@]} -gt "1" ]; then round=0 -- 2.8.0 From 90a3613d854085e1fe1416714729aa24e2013cf3 Mon Sep 17 00:00:00 2001 From: ampling <[email protected]> Date: Sat, 7 May 2016 06:14:50 -0400 Subject: [PATCH 11/11] Improved filelock, and more appropriate variable names. --- contrib/dmenu/passmenu | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu index e6e2cfc..68b4150 100755 --- a/contrib/dmenu/passmenu +++ b/contrib/dmenu/passmenu @@ -17,50 +17,52 @@ prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}" password_files=( "$prefix"/**/*.gpg ) password_files=( "${password_files[@]#"$prefix"/}" ) password_files=( "${password_files[@]%.gpg}" ) +password=( ${password:-""} ) +before=( "${before:-}" ) +userID="$(id -u $(whoami))" +stalelock=( "${stalelock:-""}" ) +stalelock=( "$(find '/tmp' -maxdepth 1 -name "passmenulock."$userID".*" -user $(whoami) -print0 -quit)" ) && +cleanup=True || { true; cleanup=False; } _finish () { [[ -n $before ]] && - printf "$before" | xclip -sel "$X_SELECTION" -i - [[ True = "$remove" ]] && - if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then - rmdir /tmp/passmenu."$uid".* >/dev/null 2>&1 + printf "$before" | base64 -d | xclip -sel "$X_SELECTION" -i + [[ True = "$cleanup" ]] && + if compgen -G "/tmp/passmenulock.1000*" >/dev/null 2>&1 ;then + rmdir /tmp/passmenulock."$userID".* >/dev/null 2>&1 fi exit } trap _finish EXIT ## Clearing stale Xclip loops avoids a posible race condition. -uid="$(id -u $(whoami))" -remove=False -if compgen -G "/tmp/passmenu.1000*" >/dev/null 2>&1 ;then - stale="$(find /tmp/passmenu."$uid".* -print -quit | - sed -e "s/\/tmp\/passmenu\.[0-9]\{1,6\}\?\..*\.//g")" - report="$(ps -u $(id -u $(whoami)) aux | grep "bash" | - grep "passmenu" | grep -v "$$")" - if [[ "$report" == *"$stale"* ]] ;then - kill "$stale" && rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1 +if test -n "$stalelock" ;then + report=( "$(ps -u $(id -u $(whoami)) aux | grep "bash" | + grep "passmenu" | grep -v "$$")" ) + stalePID=( "$(printf $stalelock | + sed -e "s/\/tmp\/passmenulock\.[0-9]\{1,6\}\?\..*\.//g")" ) + if [[ "$report" == *"$stalePID"* ]] ;then + kill "$stalePID" || exit 1 else - rmdir /tmp/passmenu.$uid.* >/dev/null 2>&1 || exit 1 + rmdir /tmp/passmenulock.$userID.* >/dev/null 2>&1 || exit 1 fi fi ## dmenu exits on KeyPress not KeyRelease. -## It would be nice to send KeyRelease event to some dummy window. -## psydocode: xdotool getwindowfocus; create dummy window; -## exec dmenu; close dummy window; restore focus. -password=${password:-} +# It would be nice to send KeyRelease event to some dummy window. +# psydocode: xdotool getwindowfocus; create dummy window; +# exec dmenu; close dummy window; restore focus. for password in $(printf '%s\n' "${password_files[@]}" | dmenu -f "$@"); do passel+=("$password") done -before=${before:-} [[ -n $password ]] || exit -before="$(xclip -sel "$X_SELECTION" -o >/dev/null 2>&1)" || true +# It would be nice to first somehow test if string exists. +before="$(xclip -sel "$X_SELECTION" -o 2>/dev/null | base64)" || true umask 077 -mktemp -d "/tmp/passmenu."$uid".XXXXXXXXXX"."$$" >/dev/null 2>&1 || -{ echo >&2 ":: Unable to make a filelock."; exit 1; } -remove=True +( mktemp -d "/tmp/passmenulock."$userID".XXXXXXXXXX"."$$" >/dev/null 2>&1 && cleanup=True || + { echo >&2 ":: Unable to make a filelock."; exit 1; } ) if [[ $typeit -eq 0 ]]; then if [ ${#passel[@]} -gt "1" ]; then -- 2.8.0
signature.asc
Description: signature
_______________________________________________ Password-Store mailing list [email protected] http://lists.zx2c4.com/mailman/listinfo/password-store
