MINOR: tools: add helpers to backup/clean/restore env

'setenv', 'presetenv', 'unsetenv', 'resetenv' keywords in configuration could
modify the process runtime environment. In case of master-worker mode this
creates a problem, as the configuration is read only once before the forking a
worker and then the master process does the reexec without reading any config
files, just to free the memory. So, during the reload a new worker process will
be created, but it will inherited the previous unchanged environment from the
master in wait mode, thus it won't benefit the changes in configuration,
related to '*env' keywords. This may cause unexpected behavior or some parser
errors in master-worker mode.

So, let's add a helper to backup all process env variables just before it will
read its configuration. And let's also add helpers to clean up the current
runtime environment and to restore it to its initial state (as it was before
parsing the config).
This commit is contained in:
Valentine Krasnobaeva 2024-08-21 19:06:33 +02:00 committed by Willy Tarreau
parent 960d68a5af
commit 1811d2a6ba
2 changed files with 126 additions and 0 deletions

View File

@ -1220,4 +1220,9 @@ void vma_set_name_id(void *addr, size_t size, const char *type, const char *name
/* cfgparse helpers */
char *fgets_from_mem(char* buf, int size, const char **position, const char *end);
/* helpers to backup/clean/restore process env */
int backup_env(void);
int clean_env(void);
int restore_env(void);
#endif /* _HAPROXY_TOOLS_H */

View File

@ -6706,6 +6706,127 @@ char *fgets_from_mem(char* buf, int size, const char **position, const char *end
return buf;
}
/* Does a backup of the process environment variables. Returns 0 on success and
* -1 on failure, which can happen only due to the lack of memory.
*/
int backup_env(void)
{
char **env = environ;
char **tmp;
/* get size of **environ */
while (*env++)
;
init_env = malloc((env - environ) * sizeof(*env));
if (init_env == NULL) {
ha_alert("Cannot allocate memory to backup env variables.\n");
return -1;
}
tmp = init_env;
for (env = environ; *env; env++) {
*tmp = strdup(*env);
if (*tmp == NULL) {
ha_alert("Cannot allocate memory to backup env variable '%s'.\n",
*env);
return -1;
}
tmp++;
}
/* last entry */
*tmp = NULL;
return 0;
}
/* Unsets all variables presented in **environ. Returns 0 on success and -1 on
* failure, when the process has run out of memory. Emits warnings and continues
* if unsetenv() fails (it fails only with EINVAL) or if the parsed string
* doesn't contain "=" (the latter is mandatory format for strings kept in
* **environ). This allows to terminate the process at the startup stage, if it
* was launched in zero-warning mode and there are some problems with
* environment.
*/
int clean_env(void)
{
char **env = environ;
char *name, *pos;
size_t name_len;
while (*env) {
pos = strchr(*env, '=');
if (pos)
name_len = pos - *env;
else {
ha_warning("Unsupported env variable format '%s' "
"(doesn't contain '='), won't be unset.\n",
*env);
continue;
}
name = my_strndup(*env, name_len);
if (name == NULL) {
ha_alert("Cannot allocate memory to parse env variable: '%s'.\n",
*env);
return -1;
}
if (unsetenv(name) != 0)
ha_warning("unsetenv() fails for '%s': %s.\n",
name, strerror(errno));
free(name);
}
return 0;
}
/* Restores **environ from backup created by backup_env(). Must be always
* preceded by clean_env() in order to properly restore the process environment.
* global init_env ptr array must be freed by the upper layers.
* Returns 0 on sucess and -1 in case if the process has run out of memory. If
* setenv() fails with EINVAL or the parsed string doesn't contain '=' (the
* latter is mandatory format for strings kept in **environ), emits warning and
* continues. This allows to terminate the process at the startup stage, if it
* was launched in zero-warning mode and there are some problems with
* environment.
*/
int restore_env(void)
{
char **env = init_env;
char *pos;
char *value;
BUG_ON(!init_env, "Triggered in restore_env(): must be preceded by "
"backup_env(), which allocates init_env.\n");
while (*env) {
pos = strchr(*env, '=');
if (!pos) {
ha_warning("Unsupported env variable format '%s' "
"(doesn't contain '='), won't be restored.\n",
*env);
env++;
continue;
}
/* replace '=' with /0 to split on 'NAME' and 'VALUE' tokens */
*pos = '\0';
pos++;
value = pos;
if (setenv(*env, value, 1) != 0) {
if (errno == EINVAL)
ha_warning("setenv() fails for '%s'='%s': %s.\n",
*env, value, strerror(errno));
else {
ha_alert("Cannot allocate memory to set env variable: '%s'.\n",
*env);
return -1;
}
}
env++;
}
return 0;
}
/*
* Local variables:
* c-indent-level: 8