Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
getenv() caching bug
- X-seq: zsh-workers 41379
- From: "Guillaume Maudoux (Layus)" <layus.on@xxxxxxxxx>
- To: bug-ncurses@xxxxxxx
- Subject: getenv() caching bug
- Date: Fri, 30 Jun 2017 14:05:44 +0200
- Cc: zsh-workers@xxxxxxx
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:from:to:references:cc:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=56Na8o8w6JZLYj4FhhK3iLX57ezfGgWm4vUExGw20P8=; b=McHzKrWBA0u04ihFAxn6Or8AmD6I/8v0GMf4g9DhdkozqSTOmnChZ/s76HZZr9upJ8 TcIPox6kG8DHot1ksGcZz1SPv1T5gMuLnhCrYNKMWNpaxf2rTXnauVG2YiuO6HKO25l7 G1BrVnqoaVXMfaEJs3j3tdJQlcjMUCqoxfqQk2URwZ1bXIb5xrMm/yLgKpG+epS6hORV KlP7JzKaFsc022hkPDT5Ykp8LzoJyBp4zd19mc1kLjyfDLSYd4qUf1T2EpV/lfbBnAac LBq5Vfsr70ypZ53NPz3Ve3Fbkz4HbmNCPd6V6qhFfjifl72TkKI7ibv4g4UaYB1lTOfq tfOQ==
- In-reply-to: <45762363-3882-27b8-8433-63ccfdc0b8be@gmail.com>
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
- References: <2ad57c3b-f0c4-afd9-4789-ae50571c63e1@gmail.com> <170627143044.ZM8536@torch.brasslantern.com> <45762363-3882-27b8-8433-63ccfdc0b8be@gmail.com>
Hi,
I have recently investigated a longstanding issue where zsh would not
initialize the terminal correctly over ssh.
In our setup, TERMINFO_DIRS was required, and defined in zshrc.
Due to incorrect getenv caching, this definition was not taken into
account, even when the terminal was reinitialized by zsh.
Zsh reinitializes the terminal after any assignment to TERM{,INFO,{_DIRS}}.
This led to the weird behavior of having the shell correctly recognised
only when TERM* variables were assigned at least one second after the
first TERMINFO_DIRS export in zshrc.
Caching was wrong because it did not remember the name of unset
variables, making it impossible to detect their appearance in the
environment in `cache_expired()`.
A patch is attached to this email.
Please tell me if it needs updates.
Regards,
-- Layus.
On 30/06/17 13:49, Guillaume Maudoux (Layus) wrote:
Thanks Bart,
I have found the origin of this issue.
There are no threads indeed, but there is caching in ncurses for
environment variables.
Due to a bug in ncurses' code, it failed to detect that the
environment var changed,
and so failed to find the terminfo database.
I will send a patch upstream, with this list in cc.
Zsh could avoid that by initializing the term as late as possible.
Calling init_term before parsing zshrc, while nothing required to
write in the terminal is premature.
Fish for example delays initializing the terminal until the first write.
Well, this would have avoided my issue, but it is not sufficient in
the general case anyway.
Thanks again for your answer!
Regards,
-- Layus.
On 27/06/17 23:30, Bart Schaefer wrote:
On Jun 27, 3:52pm, Guillaume Maudoux (Layus) wrote:
}
} In my case, when TERMINFO_DIRS is set from /etc/zshenv, the new
value is
} ignored by ncurses.
} The same applies to the subsequent TERM=$TERM in /etc/zshenv.
What does "the same" mean here?
} However, if I add a delay before TERM=$TERM, then the TERMINFO_DIRS
} update is seen, and the terminal is properly detected.
}
} Could it be that there are threads involved in zsh ?
There aren't threads, but there are signal handlers. We've had issues
before where certain terminal emulators will fire signals at the process
they are controlling at, shall we say, inopportune times.
However, the most common culprit would be the WINCH (window size change)
signal, which we block before reading init scripts and don't unblock
until ZLE is started up. And I don't know why ncurses would be doing
anything at all during /etc/zshenv, except via init_term() from those
assignments if ncurses is what provides tgetent().
Normally init_term() is called when setupvals() imports TERM from the
environment, which happens before zshenv is read. This would also be
the case for import of TERMINFO_DIRS, although if that happens before
TERM has been imported, init_term() will do nothing.
One thing I have noticed is that if TERMINFO_DIRS is in the environment
when the shell first starts up, tgetent() looks *only* there for $TERM.
Conversely if the shell starts and then TERMINFO_DIRS is assigned later,
the default definitions are also consulted. I don't know if that means
that TERMINFO_DIRS is ignored when a default has already been loaded.
This might differ in various tgetent() implementations.
Also, the "can't find terminal definition for ..." message is suppressed
during import from the environment.
If TERM is not in the environment
Aside: Does it matter that terminfodirssetfn() does not check (x == 0)
except before adding to the environment?
diff --git a/ncurses/tinfo/db_iterator.c b/ncurses/tinfo/db_iterator.c
index 94a4082047..0549dae224 100644
--- a/ncurses/tinfo/db_iterator.c
+++ b/ncurses/tinfo/db_iterator.c
@@ -92,33 +92,33 @@ check_existence(const char *name, struct stat *sb)
* Store the latest value of an environment variable in my_vars[] so we can
* detect if one changes, invalidating the cached search-list.
*/
static bool
update_getenv(const char *name, DBDIRS which)
{
bool result = FALSE;
if (which < dbdLAST) {
char *value;
+ char *cached_value = my_vars[which].value;
+ bool same_value;
- if ((value = getenv(name)) == 0 || (value = strdup(value)) == 0) {
- ;
- } else if (my_vars[which].name == 0 || strcmp(my_vars[which].name, name)) {
- FreeIfNeeded(my_vars[which].value);
- my_vars[which].name = name;
- my_vars[which].value = value;
- result = TRUE;
- } else if ((my_vars[which].value != 0) ^ (value != 0)) {
- FreeIfNeeded(my_vars[which].value);
- my_vars[which].value = value;
- result = TRUE;
- } else if (value != 0 && strcmp(value, my_vars[which].value)) {
+ if ((value = getenv(name)) != 0) {
+ value = strdup(value);
+ }
+ same_value = (value == 0 && cached_value == 0)
+ || (value != 0 && cached_value != 0 && strcmp(value, cached_value) == 0);
+
+ // Update var name for later checks
+ my_vars[which].name = name;
+
+ if (!same_value) {
FreeIfNeeded(my_vars[which].value);
my_vars[which].value = value;
result = TRUE;
} else {
free(value);
}
}
return result;
}
Messages sorted by:
Reverse Date,
Date,
Thread,
Author