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