1 /*
2  * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /* We use APIs that access the standard Unix environ array, which
27  * is defined by UNIX98 to look like:
28  *
29  *    char **environ;
30  *
31  * These are unsorted, case-sensitive, null-terminated arrays of bytes
32  * of the form FOO=BAR\000 which are usually encoded in the user's
33  * default encoding (file.encoding is an excellent choice for
34  * encoding/decoding these).  However, even though the user cannot
35  * directly access the underlying byte representation, we take pains
36  * to pass on the child the exact byte representation we inherit from
37  * the parent process for any environment name or value not created by
38  * Javaland.  So we keep track of all the byte representations.
39  *
40  * Internally, we define the types Variable and Value that exhibit
41  * String/byteArray duality.  The internal representation of the
42  * environment then looks like a Map<Variable,Value>.  But we don't
43  * expose this to the user -- we only provide a Map<String,String>
44  * view, although we could also provide a Map<byte[],byte[]> view.
45  *
46  * The non-private methods in this class are not for general use even
47  * within this package.  Instead, they are the system-dependent parts
48  * of the system-independent method of the same name.  Don't even
49  * think of using this class unless your method's name appears below.
50  *
51  * @author  Martin Buchholz
52  * @since   1.5
53  */
54 
55 package java.lang;
56 
57 import java.io.*;
58 import java.util.*;
59 
60 
61 final class ProcessEnvironment
62 {
63     // BEGIN Android-removed: Don't cache the C environment.  http://b/201665416
64     // private static final HashMap<Variable,Value> theEnvironment;
65     // private static final Map<String,String> theUnmodifiableEnvironment;
66     // END Android-removed: Don't cache the C environment.  http://b/201665416
67     static final int MIN_NAME_LENGTH = 0;
68 
69     // BEGIN Android-removed: Don't cache the C environment.  http://b/201665416
70     /*
71     static {
72         // We cache the C environment.  This means that subsequent calls
73         // to putenv/setenv from C will not be visible from Java code.
74         byte[][] environ = environ();
75         theEnvironment = new HashMap<>(environ.length/2 + 3);
76         // Read environment variables back to front,
77         // so that earlier variables override later ones.
78         for (int i = environ.length-1; i > 0; i-=2)
79             theEnvironment.put(Variable.valueOf(environ[i-1]),
80                                Value.valueOf(environ[i]));
81 
82         theUnmodifiableEnvironment
83             = Collections.unmodifiableMap
84             (new StringEnvironment(theEnvironment));
85     }
86 
87     /* Only for use by System.getenv(String) *
88     static String getenv(String name) {
89         return theUnmodifiableEnvironment.get(name);
90     }
91     */
92     // END Android-removed: Don't cache the C environment.  http://b/201665416
93 
94     // BEGIN Android-added: Don't cache the C environment.  http://b/201665416
95     // Instead, build a new copy each time using the same code as the previous
96     // static initializer.
buildEnvironment()97     private static Map<String, String> buildEnvironment() {
98         byte[][] environ = environ();
99         Map<Variable, Value> env = new HashMap<>(environ.length / 2 + 3);
100         // Read environment variables back to front,
101         // so that earlier variables override later ones.
102         for (int i = environ.length-1; i > 0; i-=2)
103             env.put(Variable.valueOf(environ[i-1]),
104                 Value.valueOf(environ[i]));
105         return new StringEnvironment(env);
106     }
107     // END Android-added: Don't cache the C environment.  http://b/201665416
108 
109     /* Only for use by System.getenv() */
getenv()110     static Map<String,String> getenv() {
111         // Android-changed: Don't cache the C environment.  http://b/201665416
112         // return theUnmodifiableEnvironment;
113         return Collections.unmodifiableMap(buildEnvironment());
114     }
115 
116     /* Only for use by ProcessBuilder.environment() */
117     @SuppressWarnings("unchecked")
environment()118     static Map<String,String> environment() {
119         // Android-changed: Don't cache the C environment.  http://b/201665416
120         // return new StringEnvironment
121         //     ((Map<Variable,Value>)(theEnvironment.clone()));
122         return buildEnvironment();
123     }
124 
125     /* Only for use by Runtime.exec(...String[]envp...) */
emptyEnvironment(int capacity)126     static Map<String,String> emptyEnvironment(int capacity) {
127         return new StringEnvironment(new HashMap<Variable,Value>(capacity));
128     }
129 
environ()130     private static native byte[][] environ();
131 
132     // This class is not instantiable.
ProcessEnvironment()133     private ProcessEnvironment() {}
134 
135     // Check that name is suitable for insertion into Environment map
validateVariable(String name)136     private static void validateVariable(String name) {
137         if (name.indexOf('=')      != -1 ||
138             name.indexOf('\u0000') != -1)
139             throw new IllegalArgumentException
140                 ("Invalid environment variable name: \"" + name + "\"");
141     }
142 
143     // Check that value is suitable for insertion into Environment map
validateValue(String value)144     private static void validateValue(String value) {
145         if (value.indexOf('\u0000') != -1)
146             throw new IllegalArgumentException
147                 ("Invalid environment variable value: \"" + value + "\"");
148     }
149 
150     // A class hiding the byteArray-String duality of
151     // text data on Unixoid operating systems.
152     private static abstract class ExternalData {
153         protected final String str;
154         protected final byte[] bytes;
155 
ExternalData(String str, byte[] bytes)156         protected ExternalData(String str, byte[] bytes) {
157             this.str = str;
158             this.bytes = bytes;
159         }
160 
getBytes()161         public byte[] getBytes() {
162             return bytes;
163         }
164 
toString()165         public String toString() {
166             return str;
167         }
168 
equals(Object o)169         public boolean equals(Object o) {
170             return o instanceof ExternalData
171                 && arrayEquals(getBytes(), ((ExternalData) o).getBytes());
172         }
173 
hashCode()174         public int hashCode() {
175             return arrayHash(getBytes());
176         }
177     }
178 
179     private static class Variable
180         extends ExternalData implements Comparable<Variable>
181     {
Variable(String str, byte[] bytes)182         protected Variable(String str, byte[] bytes) {
183             super(str, bytes);
184         }
185 
valueOfQueryOnly(Object str)186         public static Variable valueOfQueryOnly(Object str) {
187             return valueOfQueryOnly((String) str);
188         }
189 
valueOfQueryOnly(String str)190         public static Variable valueOfQueryOnly(String str) {
191             return new Variable(str, str.getBytes());
192         }
193 
valueOf(String str)194         public static Variable valueOf(String str) {
195             validateVariable(str);
196             return valueOfQueryOnly(str);
197         }
198 
valueOf(byte[] bytes)199         public static Variable valueOf(byte[] bytes) {
200             return new Variable(new String(bytes), bytes);
201         }
202 
compareTo(Variable variable)203         public int compareTo(Variable variable) {
204             return arrayCompare(getBytes(), variable.getBytes());
205         }
206 
equals(Object o)207         public boolean equals(Object o) {
208             return o instanceof Variable && super.equals(o);
209         }
210     }
211 
212     private static class Value
213         extends ExternalData implements Comparable<Value>
214     {
Value(String str, byte[] bytes)215         protected Value(String str, byte[] bytes) {
216             super(str, bytes);
217         }
218 
valueOfQueryOnly(Object str)219         public static Value valueOfQueryOnly(Object str) {
220             return valueOfQueryOnly((String) str);
221         }
222 
valueOfQueryOnly(String str)223         public static Value valueOfQueryOnly(String str) {
224             return new Value(str, str.getBytes());
225         }
226 
valueOf(String str)227         public static Value valueOf(String str) {
228             validateValue(str);
229             return valueOfQueryOnly(str);
230         }
231 
valueOf(byte[] bytes)232         public static Value valueOf(byte[] bytes) {
233             return new Value(new String(bytes), bytes);
234         }
235 
compareTo(Value value)236         public int compareTo(Value value) {
237             return arrayCompare(getBytes(), value.getBytes());
238         }
239 
equals(Object o)240         public boolean equals(Object o) {
241             return o instanceof Value && super.equals(o);
242         }
243     }
244 
245     // This implements the String map view the user sees.
246     private static class StringEnvironment
247         extends AbstractMap<String,String>
248     {
249         private Map<Variable,Value> m;
toString(Value v)250         private static String toString(Value v) {
251             return v == null ? null : v.toString();
252         }
StringEnvironment(Map<Variable,Value> m)253         public StringEnvironment(Map<Variable,Value> m) {this.m = m;}
size()254         public int size()        {return m.size();}
isEmpty()255         public boolean isEmpty() {return m.isEmpty();}
clear()256         public void clear()      {       m.clear();}
containsKey(Object key)257         public boolean containsKey(Object key) {
258             return m.containsKey(Variable.valueOfQueryOnly(key));
259         }
containsValue(Object value)260         public boolean containsValue(Object value) {
261             return m.containsValue(Value.valueOfQueryOnly(value));
262         }
get(Object key)263         public String get(Object key) {
264             return toString(m.get(Variable.valueOfQueryOnly(key)));
265         }
put(String key, String value)266         public String put(String key, String value) {
267             return toString(m.put(Variable.valueOf(key),
268                                   Value.valueOf(value)));
269         }
remove(Object key)270         public String remove(Object key) {
271             return toString(m.remove(Variable.valueOfQueryOnly(key)));
272         }
keySet()273         public Set<String> keySet() {
274             return new StringKeySet(m.keySet());
275         }
entrySet()276         public Set<Map.Entry<String,String>> entrySet() {
277             return new StringEntrySet(m.entrySet());
278         }
values()279         public Collection<String> values() {
280             return new StringValues(m.values());
281         }
282 
283         // It is technically feasible to provide a byte-oriented view
284         // as follows:
285         //      public Map<byte[],byte[]> asByteArrayMap() {
286         //          return new ByteArrayEnvironment(m);
287         //      }
288 
289 
290         // Convert to Unix style environ as a monolithic byte array
291         // inspired by the Windows Environment Block, except we work
292         // exclusively with bytes instead of chars, and we need only
293         // one trailing NUL on Unix.
294         // This keeps the JNI as simple and efficient as possible.
toEnvironmentBlock(int[]envc)295         public byte[] toEnvironmentBlock(int[]envc) {
296             int count = m.size() * 2; // For added '=' and NUL
297             for (Map.Entry<Variable,Value> entry : m.entrySet()) {
298                 count += entry.getKey().getBytes().length;
299                 count += entry.getValue().getBytes().length;
300             }
301 
302             byte[] block = new byte[count];
303 
304             int i = 0;
305             for (Map.Entry<Variable,Value> entry : m.entrySet()) {
306                 byte[] key   = entry.getKey  ().getBytes();
307                 byte[] value = entry.getValue().getBytes();
308                 System.arraycopy(key, 0, block, i, key.length);
309                 i+=key.length;
310                 block[i++] = (byte) '=';
311                 System.arraycopy(value, 0, block, i, value.length);
312                 i+=value.length + 1;
313                 // No need to write NUL byte explicitly
314                 //block[i++] = (byte) '\u0000';
315             }
316             envc[0] = m.size();
317             return block;
318         }
319     }
320 
toEnvironmentBlock(Map<String,String> map, int[]envc)321     static byte[] toEnvironmentBlock(Map<String,String> map, int[]envc) {
322         return map == null ? null :
323             ((StringEnvironment)map).toEnvironmentBlock(envc);
324     }
325 
326 
327     private static class StringEntry
328         implements Map.Entry<String,String>
329     {
330         private final Map.Entry<Variable,Value> e;
StringEntry(Map.Entry<Variable,Value> e)331         public StringEntry(Map.Entry<Variable,Value> e) {this.e = e;}
getKey()332         public String getKey()   {return e.getKey().toString();}
getValue()333         public String getValue() {return e.getValue().toString();}
setValue(String newValue)334         public String setValue(String newValue) {
335             return e.setValue(Value.valueOf(newValue)).toString();
336         }
toString()337         public String toString() {return getKey() + "=" + getValue();}
equals(Object o)338         public boolean equals(Object o) {
339             return o instanceof StringEntry
340                 && e.equals(((StringEntry)o).e);
341         }
hashCode()342         public int hashCode()    {return e.hashCode();}
343     }
344 
345     private static class StringEntrySet
346         extends AbstractSet<Map.Entry<String,String>>
347     {
348         private final Set<Map.Entry<Variable,Value>> s;
StringEntrySet(Set<Map.Entry<Variable,Value>> s)349         public StringEntrySet(Set<Map.Entry<Variable,Value>> s) {this.s = s;}
size()350         public int size()        {return s.size();}
isEmpty()351         public boolean isEmpty() {return s.isEmpty();}
clear()352         public void clear()      {       s.clear();}
iterator()353         public Iterator<Map.Entry<String,String>> iterator() {
354             return new Iterator<Map.Entry<String,String>>() {
355                 Iterator<Map.Entry<Variable,Value>> i = s.iterator();
356                 public boolean hasNext() {return i.hasNext();}
357                 public Map.Entry<String,String> next() {
358                     return new StringEntry(i.next());
359                 }
360                 public void remove() {i.remove();}
361             };
362         }
vvEntry(final Object o)363         private static Map.Entry<Variable,Value> vvEntry(final Object o) {
364             if (o instanceof StringEntry)
365                 return ((StringEntry)o).e;
366             return new Map.Entry<Variable,Value>() {
367                 public Variable getKey() {
368                     return Variable.valueOfQueryOnly(((Map.Entry)o).getKey());
369                 }
370                 public Value getValue() {
371                     return Value.valueOfQueryOnly(((Map.Entry)o).getValue());
372                 }
373                 public Value setValue(Value value) {
374                     throw new UnsupportedOperationException();
375                 }
376             };
377         }
378         public boolean contains(Object o) { return s.contains(vvEntry(o)); }
379         public boolean remove(Object o)   { return s.remove(vvEntry(o)); }
380         public boolean equals(Object o) {
381             return o instanceof StringEntrySet
382                 && s.equals(((StringEntrySet) o).s);
383         }
384         public int hashCode() {return s.hashCode();}
385     }
386 
387     private static class StringValues
388           extends AbstractCollection<String>
389     {
390         private final Collection<Value> c;
391         public StringValues(Collection<Value> c) {this.c = c;}
392         public int size()        {return c.size();}
393         public boolean isEmpty() {return c.isEmpty();}
394         public void clear()      {       c.clear();}
395         public Iterator<String> iterator() {
396             return new Iterator<String>() {
397                 Iterator<Value> i = c.iterator();
398                 public boolean hasNext() {return i.hasNext();}
399                 public String next()     {return i.next().toString();}
400                 public void remove()     {i.remove();}
401             };
402         }
403         public boolean contains(Object o) {
404             return c.contains(Value.valueOfQueryOnly(o));
405         }
406         public boolean remove(Object o) {
407             return c.remove(Value.valueOfQueryOnly(o));
408         }
409         public boolean equals(Object o) {
410             return o instanceof StringValues
411                 && c.equals(((StringValues)o).c);
412         }
413         public int hashCode() {return c.hashCode();}
414     }
415 
416     private static class StringKeySet extends AbstractSet<String> {
417         private final Set<Variable> s;
418         public StringKeySet(Set<Variable> s) {this.s = s;}
419         public int size()        {return s.size();}
420         public boolean isEmpty() {return s.isEmpty();}
421         public void clear()      {       s.clear();}
422         public Iterator<String> iterator() {
423             return new Iterator<String>() {
424                 Iterator<Variable> i = s.iterator();
425                 public boolean hasNext() {return i.hasNext();}
426                 public String next()     {return i.next().toString();}
427                 public void remove()     {       i.remove();}
428             };
429         }
430         public boolean contains(Object o) {
431             return s.contains(Variable.valueOfQueryOnly(o));
432         }
433         public boolean remove(Object o) {
434             return s.remove(Variable.valueOfQueryOnly(o));
435         }
436     }
437 
438     // Replace with general purpose method someday
439     private static int arrayCompare(byte[]x, byte[] y) {
440         int min = x.length < y.length ? x.length : y.length;
441         for (int i = 0; i < min; i++)
442             if (x[i] != y[i])
443                 return x[i] - y[i];
444         return x.length - y.length;
445     }
446 
447     // Replace with general purpose method someday
448     private static boolean arrayEquals(byte[] x, byte[] y) {
449         if (x.length != y.length)
450             return false;
451         for (int i = 0; i < x.length; i++)
452             if (x[i] != y[i])
453                 return false;
454         return true;
455     }
456 
457     // Replace with general purpose method someday
458     private static int arrayHash(byte[] x) {
459         int hash = 0;
460         for (int i = 0; i < x.length; i++)
461             hash = 31 * hash + x[i];
462         return hash;
463     }
464 
465 }
466