]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: tools: add helpers to backup/clean/restore env
authorValentine Krasnobaeva <vkrasnobaeva@haproxy.com>
Wed, 21 Aug 2024 17:06:33 +0000 (19:06 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 23 Aug 2024 15:06:33 +0000 (17:06 +0200)
'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).

include/haproxy/tools.h
src/tools.c

index adadee5c51669054b8ac16ba2c3c8ee3eeeac809..fd078c7daf8d0dff973a593fc376863469f59a76 100644 (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 */
index c1acea3341ef5ad48d7dc7283fce314a5d45e52e..2b7c1316539fdef773604b0c4d5092367ffb3ff0 100644 (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