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     private static final HashMap<Variable,Value> theEnvironment;
64     private static final Map<String,String> theUnmodifiableEnvironment;
65     static final int MIN_NAME_LENGTH = 0;
66
67     static {
68         // We cache the C environment.  This means that subsequent calls
69         // to putenv/setenv from C will not be visible from Java code.
70         byte[][] environ = environ();
71         theEnvironment = new HashMap<>(environ.length/2 + 3);
72         // Read environment variables back to front,
73         // so that earlier variables override later ones.
74         for (int i = environ.length-1; i > 0; i-=2)
75             theEnvironment.put(Variable.valueOf(environ[i-1]),
76                                Value.valueOf(environ[i]));
77
78         theUnmodifiableEnvironment
79             = Collections.unmodifiableMap
80             (new StringEnvironment(theEnvironment));
81     }
82
83     /* Only for use by System.getenv(String) */
84     static String getenv(String name) {
85         return theUnmodifiableEnvironment.get(name);
86     }
87
88     /* Only for use by System.getenv() */
89     static Map<String,String> getenv() {
90         return theUnmodifiableEnvironment;
91     }
92
93     /* Only for use by ProcessBuilder.environment() */
94     @SuppressWarnings("unchecked")
95     static Map<String,String> environment() {
96         return new StringEnvironment
97             ((Map<Variable,Value>)(theEnvironment.clone()));
98     }
99
100     /* Only for use by Runtime.exec(...String[]envp...) */
101     static Map<String,String> emptyEnvironment(int capacity) {
102         return new StringEnvironment(new HashMap<>(capacity));
103     }
104
105     private static native byte[][] environ();
106
107     // This class is not instantiable.
108     private ProcessEnvironment() {}
109
110     // Check that name is suitable for insertion into Environment map
111     private static void validateVariable(String name) {
112         if (name.indexOf('=')      != -1 ||
113             name.indexOf('\u0000') != -1)
114             throw new IllegalArgumentException
115                 ("Invalid environment variable name: \"" + name + "\"");
116     }
117
118     // Check that value is suitable for insertion into Environment map
119     private static void validateValue(String value) {
120         if (value.indexOf('\u0000') != -1)
121             throw new IllegalArgumentException
122                 ("Invalid environment variable value: \"" + value + "\"");
123     }
124
125     // A class hiding the byteArray-String duality of
126     // text data on Unixoid operating systems.
127     private abstract static class ExternalData {
128         protected final String str;
129         protected final byte[] bytes;
130
131         protected ExternalData(String str, byte[] bytes) {
132             this.str = str;
133             this.bytes = bytes;
134         }
135
136         public byte[] getBytes() {
137             return bytes;
138         }
139
140         public String toString() {
141             return str;
142         }
143
144         public boolean equals(Object o) {
145             return o instanceof ExternalData
146                 && arrayEquals(getBytes(), ((ExternalData) o).getBytes());
147         }
148
149         public int hashCode() {
150             return arrayHash(getBytes());
151         }
152     }
153
154     private static class Variable
155         extends ExternalData implements Comparable<Variable>
156     {
157         protected Variable(String str, byte[] bytes) {
158             super(str, bytes);
159         }
160
161         public static Variable valueOfQueryOnly(Object str) {
162             return valueOfQueryOnly((String) str);
163         }
164
165         public static Variable valueOfQueryOnly(String str) {
166             return new Variable(str, str.getBytes());
167         }
168
169         public static Variable valueOf(String str) {
170             validateVariable(str);
171             return valueOfQueryOnly(str);
172         }
173
174         public static Variable valueOf(byte[] bytes) {
175             return new Variable(new String(bytes), bytes);
176         }
177
178         public int compareTo(Variable variable) {
179             return arrayCompare(getBytes(), variable.getBytes());
180         }
181
182         public boolean equals(Object o) {
183             return o instanceof Variable && super.equals(o);
184         }
185     }
186
187     private static class Value
188         extends ExternalData implements Comparable<Value>
189     {
190         protected Value(String str, byte[] bytes) {
191             super(str, bytes);
192         }
193
194         public static Value valueOfQueryOnly(Object str) {
195             return valueOfQueryOnly((String) str);
196         }
197
198         public static Value valueOfQueryOnly(String str) {
199             return new Value(str, str.getBytes());
200         }
201
202         public static Value valueOf(String str) {
203             validateValue(str);
204             return valueOfQueryOnly(str);
205         }
206
207         public static Value valueOf(byte[] bytes) {
208             return new Value(new String(bytes), bytes);
209         }
210
211         public int compareTo(Value value) {
212             return arrayCompare(getBytes(), value.getBytes());
213         }
214
215         public boolean equals(Object o) {
216             return o instanceof Value && super.equals(o);
217         }
218     }
219
220     // This implements the String map view the user sees.
221     private static class StringEnvironment
222         extends AbstractMap<String,String>
223     {
224         private Map<Variable,Value> m;
225         private static String toString(Value v) {
226             return v == null ? null : v.toString();
227         }
228         public StringEnvironment(Map<Variable,Value> m) {this.m = m;}
229         public int size()        {return m.size();}
230         public boolean isEmpty() {return m.isEmpty();}
231         public void clear()      {       m.clear();}
232         public boolean containsKey(Object key) {
233             return m.containsKey(Variable.valueOfQueryOnly(key));
234         }
235         public boolean containsValue(Object value) {
236             return m.containsValue(Value.valueOfQueryOnly(value));
237         }
238         public String get(Object key) {
239             return toString(m.get(Variable.valueOfQueryOnly(key)));
240         }
241         public String put(String key, String value) {
242             return toString(m.put(Variable.valueOf(key),
243                                   Value.valueOf(value)));
244         }
245         public String remove(Object key) {
246             return toString(m.remove(Variable.valueOfQueryOnly(key)));
247         }
248         public Set<String> keySet() {
249             return new StringKeySet(m.keySet());
250         }
251         public Set<Map.Entry<String,String>> entrySet() {
252             return new StringEntrySet(m.entrySet());
253         }
254         public Collection<String> values() {
255             return new StringValues(m.values());
256         }
257
258         // It is technically feasible to provide a byte-oriented view
259         // as follows:
260         //      public Map<byte[],byte[]> asByteArrayMap() {
261         //          return new ByteArrayEnvironment(m);
262         //      }
263
264
265         // Convert to Unix style environ as a monolithic byte array
266         // inspired by the Windows Environment Block, except we work
267         // exclusively with bytes instead of chars, and we need only
268         // one trailing NUL on Unix.
269         // This keeps the JNI as simple and efficient as possible.
270         public byte[] toEnvironmentBlock(int[]envc) {
271             int count = m.size() * 2; // For added '=' and NUL
272             for (Map.Entry<Variable,Value> entry : m.entrySet()) {
273                 count += entry.getKey().getBytes().length;
274                 count += entry.getValue().getBytes().length;
275             }
276
277             byte[] block = new byte[count];
278
279             int i = 0;
280             for (Map.Entry<Variable,Value> entry : m.entrySet()) {
281                 byte[] key   = entry.getKey  ().getBytes();
282                 byte[] value = entry.getValue().getBytes();
283                 System.arraycopy(key, 0, block, i, key.length);
284                 i+=key.length;
285                 block[i++] = (byte) '=';
286                 System.arraycopy(value, 0, block, i, value.length);
287                 i+=value.length + 1;
288                 // No need to write NUL byte explicitly
289                 //block[i++] = (byte) '\u0000';
290             }
291             envc[0] = m.size();
292             return block;
293         }
294     }
295
296     static byte[] toEnvironmentBlock(Map<String,String> map, int[]envc) {
297         return map == null ? null :
298             ((StringEnvironment)map).toEnvironmentBlock(envc);
299     }
300
301
302     private static class StringEntry
303         implements Map.Entry<String,String>
304     {
305         private final Map.Entry<Variable,Value> e;
306         public StringEntry(Map.Entry<Variable,Value> e) {this.e = e;}
307         public String getKey()   {return e.getKey().toString();}
308         public String getValue() {return e.getValue().toString();}
309         public String setValue(String newValue) {
310             return e.setValue(Value.valueOf(newValue)).toString();
311         }
312         public String toString() {return getKey() + "=" + getValue();}
313         public boolean equals(Object o) {
314             return o instanceof StringEntry
315                 && e.equals(((StringEntry)o).e);
316         }
317         public int hashCode()    {return e.hashCode();}
318     }
319
320     private static class StringEntrySet
321         extends AbstractSet<Map.Entry<String,String>>
322     {
323         private final Set<Map.Entry<Variable,Value>> s;
324         public StringEntrySet(Set<Map.Entry<Variable,Value>> s) {this.s = s;}
325         public int size()        {return s.size();}
326         public boolean isEmpty() {return s.isEmpty();}
327         public void clear()      {       s.clear();}
328         public Iterator<Map.Entry<String,String>> iterator() {
329             return new Iterator<Map.Entry<String,String>>() {
330                 Iterator<Map.Entry<Variable,Value>> i = s.iterator();
331                 public boolean hasNext() {return i.hasNext();}
332                 public Map.Entry<String,String> next() {
333                     return new StringEntry(i.next());
334                 }
335                 public void remove() {i.remove();}
336             };
337         }
338         private static Map.Entry<Variable,Value> vvEntry(final Object o) {
339             if (o instanceof StringEntry)
340                 return ((StringEntry)o).e;
341             return new Map.Entry<Variable,Value>() {
342                 public Variable getKey() {
343                     return Variable.valueOfQueryOnly(((Map.Entry)o).getKey());
344                 }
345                 public Value getValue() {
346                     return Value.valueOfQueryOnly(((Map.Entry)o).getValue());
347                 }
348                 public Value setValue(Value value) {
349                     throw new UnsupportedOperationException();
350                 }
351             };
352         }
353         public boolean contains(Object o) { return s.contains(vvEntry(o)); }
354         public boolean remove(Object o)   { return s.remove(vvEntry(o)); }
355         public boolean equals(Object o) {
356             return o instanceof StringEntrySet
357                 && s.equals(((StringEntrySet) o).s);
358         }
359         public int hashCode() {return s.hashCode();}
360     }
361
362     private static class StringValues
363           extends AbstractCollection<String>
364     {
365         private final Collection<Value> c;
366         public StringValues(Collection<Value> c) {this.c = c;}
367         public int size()        {return c.size();}
368         public boolean isEmpty() {return c.isEmpty();}
369         public void clear()      {       c.clear();}
370         public Iterator<String> iterator() {
371             return new Iterator<String>() {
372                 Iterator<Value> i = c.iterator();
373                 public boolean hasNext() {return i.hasNext();}
374                 public String next()     {return i.next().toString();}
375                 public void remove()     {i.remove();}
376             };
377         }
378         public boolean contains(Object o) {
379             return c.contains(Value.valueOfQueryOnly(o));
380         }
381         public boolean remove(Object o) {
382             return c.remove(Value.valueOfQueryOnly(o));
383         }
384         public boolean equals(Object o) {
385             return o instanceof StringValues
386                 && c.equals(((StringValues)o).c);
387         }
388         public int hashCode() {return c.hashCode();}
389     }
390
391     private static class StringKeySet extends AbstractSet<String> {
392         private final Set<Variable> s;
393         public StringKeySet(Set<Variable> s) {this.s = s;}
394         public int size()        {return s.size();}
395         public boolean isEmpty() {return s.isEmpty();}
396         public void clear()      {       s.clear();}
397         public Iterator<String> iterator() {
398             return new Iterator<String>() {
399                 Iterator<Variable> i = s.iterator();
400                 public boolean hasNext() {return i.hasNext();}
401                 public String next()     {return i.next().toString();}
402                 public void remove()     {       i.remove();}
403             };
404         }
405         public boolean contains(Object o) {
406             return s.contains(Variable.valueOfQueryOnly(o));
407         }
408         public boolean remove(Object o) {
409             return s.remove(Variable.valueOfQueryOnly(o));
410         }
411     }
412
413     // Replace with general purpose method someday
414     private static int arrayCompare(byte[]x, byte[] y) {
415         int min = x.length < y.length ? x.length : y.length;
416         for (int i = 0; i < min; i++)
417             if (x[i] != y[i])
418                 return x[i] - y[i];
419         return x.length - y.length;
420     }
421
422     // Replace with general purpose method someday
423     private static boolean arrayEquals(byte[] x, byte[] y) {
424         if (x.length != y.length)
425             return false;
426         for (int i = 0; i < x.length; i++)
427             if (x[i] != y[i])
428                 return false;
429         return true;
430     }
431
432     // Replace with general purpose method someday
433     private static int arrayHash(byte[] x) {
434         int hash = 0;
435         for (int i = 0; i < x.length; i++)
436             hash = 31 * hash + x[i];
437         return hash;
438     }
439
440 }
441