LCOV - code coverage report
Current view: top level - lib/process - env.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 60 70 85.7 %
Date: 2021-11-24 03:28:48 Functions: 5 7 71.4 %

          Line data    Source code
       1             : /* Copyright (c) 2003-2004, Roger Dingledine
       2             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       3             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       4             : /* See LICENSE for licensing information */
       5             : 
       6             : /**
       7             :  * \file env.c
       8             :  * \brief Inspect and manipulate the environment variables.
       9             :  **/
      10             : 
      11             : #include "orconfig.h"
      12             : #include "lib/process/env.h"
      13             : 
      14             : #include "lib/malloc/malloc.h"
      15             : #include "lib/ctime/di_ops.h"
      16             : #include "lib/container/smartlist.h"
      17             : #include "lib/log/util_bug.h"
      18             : #include "lib/log/log.h"
      19             : 
      20             : #ifdef HAVE_UNISTD_H
      21             : #include <unistd.h>
      22             : #endif
      23             : #include <stdlib.h>
      24             : #include <string.h>
      25             : #ifdef HAVE_CRT_EXTERNS_H
      26             : /* For _NSGetEnviron on macOS */
      27             : #include <crt_externs.h>
      28             : #endif
      29             : 
      30             : #ifndef HAVE__NSGETENVIRON
      31             : #ifndef HAVE_EXTERN_ENVIRON_DECLARED
      32             : /* Some platforms declare environ under some circumstances, others don't. */
      33             : #ifndef RUNNING_DOXYGEN
      34             : extern char **environ;
      35             : #endif
      36             : #endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */
      37             : #endif /* !defined(HAVE__NSGETENVIRON) */
      38             : 
      39             : /** Return the current environment. This is a portable replacement for
      40             :  * 'environ'. */
      41             : char **
      42           0 : get_environment(void)
      43             : {
      44             : #ifdef HAVE__NSGETENVIRON
      45             :   /* This is for compatibility between OSX versions.  Otherwise (for example)
      46             :    * when we do a mostly-static build on OSX 10.7, the resulting binary won't
      47             :    * work on OSX 10.6. */
      48             :   return *_NSGetEnviron();
      49             : #else /* !defined(HAVE__NSGETENVIRON) */
      50           0 :   return environ;
      51             : #endif /* defined(HAVE__NSGETENVIRON) */
      52             : }
      53             : 
      54             : /** Helper: return the number of characters in <b>s</b> preceding the first
      55             :  * occurrence of <b>ch</b>. If <b>ch</b> does not occur in <b>s</b>, return
      56             :  * the length of <b>s</b>. Should be equivalent to strspn(s, "ch"). */
      57             : static inline size_t
      58          86 : str_num_before(const char *s, char ch)
      59             : {
      60          86 :   const char *cp = strchr(s, ch);
      61          86 :   if (cp)
      62          69 :     return cp - s;
      63             :   else
      64          17 :     return strlen(s);
      65             : }
      66             : 
      67             : /** Return non-zero iff getenv would consider <b>s1</b> and <b>s2</b>
      68             :  * to have the same name as strings in a process's environment. */
      69             : int
      70          39 : environment_variable_names_equal(const char *s1, const char *s2)
      71             : {
      72          39 :   size_t s1_name_len = str_num_before(s1, '=');
      73          39 :   size_t s2_name_len = str_num_before(s2, '=');
      74             : 
      75          63 :   return (s1_name_len == s2_name_len &&
      76          24 :           tor_memeq(s1, s2, s1_name_len));
      77             : }
      78             : 
      79             : /** Free <b>env</b> (assuming it was produced by
      80             :  * process_environment_make). */
      81             : void
      82           3 : process_environment_free_(process_environment_t *env)
      83             : {
      84           3 :   if (env == NULL) return;
      85             : 
      86             :   /* As both an optimization hack to reduce consing on Unixoid systems
      87             :    * and a nice way to ensure that some otherwise-Windows-specific
      88             :    * code will always get tested before changes to it get merged, the
      89             :    * strings which env->unixoid_environment_block points to are packed
      90             :    * into env->windows_environment_block. */
      91           3 :   tor_free(env->unixoid_environment_block);
      92           3 :   tor_free(env->windows_environment_block);
      93             : 
      94           3 :   tor_free(env);
      95             : }
      96             : 
      97             : /** Make a process_environment_t containing the environment variables
      98             :  * specified in <b>env_vars</b> (as C strings of the form
      99             :  * "NAME=VALUE"). */
     100             : process_environment_t *
     101           5 : process_environment_make(struct smartlist_t *env_vars)
     102             : {
     103           5 :   process_environment_t *env = tor_malloc_zero(sizeof(process_environment_t));
     104           5 :   int n_env_vars = smartlist_len(env_vars);
     105           5 :   int i;
     106           5 :   size_t total_env_length;
     107           5 :   smartlist_t *env_vars_sorted;
     108             : 
     109           5 :   tor_assert(n_env_vars + 1 != 0);
     110           5 :   env->unixoid_environment_block = tor_calloc(n_env_vars + 1, sizeof(char *));
     111             :   /* env->unixoid_environment_block is already NULL-terminated,
     112             :    * because we assume that NULL == 0 (and check that during compilation). */
     113             : 
     114           5 :   total_env_length = 1; /* terminating NUL of terminating empty string */
     115          13 :   for (i = 0; i < n_env_vars; ++i) {
     116           8 :     const char *s = smartlist_get(env_vars, (int)i);
     117           8 :     size_t slen = strlen(s);
     118             : 
     119           8 :     tor_assert(slen + 1 != 0);
     120           8 :     tor_assert(slen + 1 < SIZE_MAX - total_env_length);
     121           8 :     total_env_length += slen + 1;
     122             :   }
     123             : 
     124           5 :   env->windows_environment_block = tor_malloc_zero(total_env_length);
     125             :   /* env->windows_environment_block is already
     126             :    * (NUL-terminated-empty-string)-terminated. */
     127             : 
     128             :   /* Some versions of Windows supposedly require that environment
     129             :    * blocks be sorted.  Or maybe some Windows programs (or their
     130             :    * runtime libraries) fail to look up strings in non-sorted
     131             :    * environment blocks.
     132             :    *
     133             :    * Also, sorting strings makes it easy to find duplicate environment
     134             :    * variables and environment-variable strings without an '=' on all
     135             :    * OSes, and they can cause badness.  Let's complain about those. */
     136           5 :   env_vars_sorted = smartlist_new();
     137           5 :   smartlist_add_all(env_vars_sorted, env_vars);
     138           5 :   smartlist_sort_strings(env_vars_sorted);
     139             : 
     140             :   /* Now copy the strings into the environment blocks. */
     141             :   {
     142           5 :     char *cp = env->windows_environment_block;
     143           5 :     const char *prev_env_var = NULL;
     144             : 
     145          13 :     for (i = 0; i < n_env_vars; ++i) {
     146           8 :       const char *s = smartlist_get(env_vars_sorted, (int)i);
     147           8 :       size_t slen = strlen(s);
     148           8 :       size_t s_name_len = str_num_before(s, '=');
     149             : 
     150           8 :       if (s_name_len == slen) {
     151           0 :         log_warn(LD_GENERAL,
     152             :                  "Preparing an environment containing a variable "
     153             :                  "without a value: %s",
     154             :                  s);
     155             :       }
     156          12 :       if (prev_env_var != NULL &&
     157           4 :           environment_variable_names_equal(s, prev_env_var)) {
     158           0 :         log_warn(LD_GENERAL,
     159             :                  "Preparing an environment containing two variables "
     160             :                  "with the same name: %s and %s",
     161             :                  prev_env_var, s);
     162             :       }
     163             : 
     164           8 :       prev_env_var = s;
     165             : 
     166             :       /* Actually copy the string into the environment. */
     167           8 :       memcpy(cp, s, slen+1);
     168           8 :       env->unixoid_environment_block[i] = cp;
     169           8 :       cp += slen+1;
     170             :     }
     171             : 
     172           5 :     tor_assert(cp == env->windows_environment_block + total_env_length - 1);
     173             :   }
     174             : 
     175           5 :   smartlist_free(env_vars_sorted);
     176             : 
     177           5 :   return env;
     178             : }
     179             : 
     180             : /** Return a newly allocated smartlist containing every variable in
     181             :  * this process's environment, as a NUL-terminated string of the form
     182             :  * "NAME=VALUE".  Note that on some/many/most/all OSes, the parent
     183             :  * process can put strings not of that form in our environment;
     184             :  * callers should try to not get crashed by that.
     185             :  *
     186             :  * The returned strings are heap-allocated, and must be freed by the
     187             :  * caller. */
     188             : struct smartlist_t *
     189           0 : get_current_process_environment_variables(void)
     190             : {
     191           0 :   smartlist_t *sl = smartlist_new();
     192             : 
     193           0 :   char **environ_tmp; /* Not const char ** ? Really? */
     194           0 :   for (environ_tmp = get_environment(); *environ_tmp; ++environ_tmp) {
     195           0 :     smartlist_add_strdup(sl, *environ_tmp);
     196             :   }
     197             : 
     198           0 :   return sl;
     199             : }
     200             : 
     201             : /** For each string s in <b>env_vars</b> such that
     202             :  * environment_variable_names_equal(s, <b>new_var</b>), remove it; if
     203             :  * <b>free_p</b> is non-zero, call <b>free_old</b>(s).  If
     204             :  * <b>new_var</b> contains '=', insert it into <b>env_vars</b>. */
     205             : void
     206           2 : set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
     207             :                                       const char *new_var,
     208             :                                       void (*free_old)(void*),
     209             :                                       int free_p)
     210             : {
     211          18 :   SMARTLIST_FOREACH_BEGIN(env_vars, const char *, s) {
     212          16 :     if (environment_variable_names_equal(s, new_var)) {
     213           1 :       SMARTLIST_DEL_CURRENT(env_vars, s);
     214           1 :       if (free_p) {
     215           1 :         free_old((void *)s);
     216             :       }
     217             :     }
     218          16 :   } SMARTLIST_FOREACH_END(s);
     219             : 
     220           2 :   if (strchr(new_var, '=') != NULL) {
     221           2 :     smartlist_add(env_vars, (void *)new_var);
     222             :   }
     223           2 : }

Generated by: LCOV version 1.14