From bb23b745e771a0da32a7270ab17ed387e210d58d Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Wed, 1 Mar 2017 17:41:53 +0200 Subject: [PATCH 1/2] Create tempfile with mktemp to avoid conflicts --- z.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/z.sh b/z.sh index c78f3cb..6ecb290 100644 --- a/z.sh +++ b/z.sh @@ -61,7 +61,7 @@ _z() { done # maintain the data file - local tempfile="$datafile.$RANDOM" + local tempfile="$(mktemp "${datafile}.XXXXXXXX")" _z_dirs | awk -v path="$*" -v now="$(date +%s)" -F"|" ' BEGIN { rank[path] = 1 From 1eb243cc9ede5dc58a3075d7b7adaf320bceabf9 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Wed, 1 Mar 2017 17:59:42 +0200 Subject: [PATCH 2/2] Add support for db locking in zsh (via flock) This commit ensures the database is locked before reading and updating when used in zsh. We load the zsh/system module which provides the zsystem flock command to perform advisory file locking. A separate code path is needed when updating the database entry because replacing the file (via mv) would cause any process waiting for a lock to fail. Here we simply clobber the database with the contents of the tempfile. --- z.sh | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/z.sh b/z.sh index 6ecb290..d6bc3cc 100644 --- a/z.sh +++ b/z.sh @@ -62,6 +62,13 @@ _z() { # maintain the data file local tempfile="$(mktemp "${datafile}.XXXXXXXX")" + if [ "$_Z_USE_ZSYSTEM_FLOCK" -eq 1 ]; then + # make sure datafile exists (for locking) + [ -f "$datafile" ] || touch "$datafile" + local lockfd + # grab exclusive lock (released when function exits) + zsystem flock -f lockfd "$datafile" || return + fi _z_dirs | awk -v path="$*" -v now="$(date +%s)" -F"|" ' BEGIN { rank[path] = 1 @@ -85,12 +92,20 @@ _z() { } else for( x in rank ) print x "|" rank[x] "|" time[x] } ' 2>/dev/null >| "$tempfile" - # do our best to avoid clobbering the datafile in a race condition. - if [ $? -ne 0 -a -f "$datafile" ]; then + local ret=$? + if [ "$_Z_USE_ZSYSTEM_FLOCK" -eq 1 ]; then + # replace contents of datafile with tempfile + env cat "$tempfile" >| "$datafile" + [ "$_Z_OWNER" ] && chown $_Z_OWNER:"$(id -ng $_Z_OWNER)" "$datafile" env rm -f "$tempfile" else - [ "$_Z_OWNER" ] && chown $_Z_OWNER:"$(id -ng $_Z_OWNER)" "$tempfile" - env mv -f "$tempfile" "$datafile" || env rm -f "$tempfile" + # do our best to avoid clobbering the datafile in a race condition. + if [ $ret -ne 0 -a -f "$datafile" ]; then + env rm -f "$tempfile" + else + [ "$_Z_OWNER" ] && chown $_Z_OWNER:"$(id -ng $_Z_OWNER)" "$tempfile" + env mv -f "$tempfile" "$datafile" || env rm -f "$tempfile" + fi fi # tab completion @@ -216,6 +231,14 @@ alias ${_Z_CMD:-z}='_z 2>&1' [ "$_Z_NO_RESOLVE_SYMLINKS" ] || _Z_RESOLVE_SYMLINKS="-P" +if type zmodload >/dev/null 2>&1; then + # load zsh/system for the zsystem flock builtin + zmodload zsh/system &>/dev/null + if zsystem supports flock &>/dev/null; then + _Z_USE_ZSYSTEM_FLOCK=1 + fi +fi + if type compctl >/dev/null 2>&1; then # zsh [ "$_Z_NO_PROMPT_COMMAND" ] || {