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) Variable.valueOf(environ[i-1])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) */ getenv(String name)84 static String getenv(String name) { 85 return theUnmodifiableEnvironment.get(name); 86 } 87 88 /* Only for use by System.getenv() */ getenv()89 static Map<String,String> getenv() { 90 return theUnmodifiableEnvironment; 91 } 92 93 /* Only for use by ProcessBuilder.environment() */ 94 @SuppressWarnings("unchecked") environment()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...) */ emptyEnvironment(int capacity)101 static Map<String,String> emptyEnvironment(int capacity) { 102 return new StringEnvironment(new HashMap<Variable,Value>(capacity)); 103 } 104 environ()105 private static native byte[][] environ(); 106 107 // This class is not instantiable. ProcessEnvironment()108 private ProcessEnvironment() {} 109 110 // Check that name is suitable for insertion into Environment map validateVariable(String name)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 validateValue(String value)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 static abstract class ExternalData { 128 protected final String str; 129 protected final byte[] bytes; 130 ExternalData(String str, byte[] bytes)131 protected ExternalData(String str, byte[] bytes) { 132 this.str = str; 133 this.bytes = bytes; 134 } 135 getBytes()136 public byte[] getBytes() { 137 return bytes; 138 } 139 toString()140 public String toString() { 141 return str; 142 } 143 equals(Object o)144 public boolean equals(Object o) { 145 return o instanceof ExternalData 146 && arrayEquals(getBytes(), ((ExternalData) o).getBytes()); 147 } 148 hashCode()149 public int hashCode() { 150 return arrayHash(getBytes()); 151 } 152 } 153 154 private static class Variable 155 extends ExternalData implements Comparable<Variable> 156 { Variable(String str, byte[] bytes)157 protected Variable(String str, byte[] bytes) { 158 super(str, bytes); 159 } 160 valueOfQueryOnly(Object str)161 public static Variable valueOfQueryOnly(Object str) { 162 return valueOfQueryOnly((String) str); 163 } 164 valueOfQueryOnly(String str)165 public static Variable valueOfQueryOnly(String str) { 166 return new Variable(str, str.getBytes()); 167 } 168 valueOf(String str)169 public static Variable valueOf(String str) { 170 validateVariable(str); 171 return valueOfQueryOnly(str); 172 } 173 valueOf(byte[] bytes)174 public static Variable valueOf(byte[] bytes) { 175 return new Variable(new String(bytes), bytes); 176 } 177 compareTo(Variable variable)178 public int compareTo(Variable variable) { 179 return arrayCompare(getBytes(), variable.getBytes()); 180 } 181 equals(Object o)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 { Value(String str, byte[] bytes)190 protected Value(String str, byte[] bytes) { 191 super(str, bytes); 192 } 193 valueOfQueryOnly(Object str)194 public static Value valueOfQueryOnly(Object str) { 195 return valueOfQueryOnly((String) str); 196 } 197 valueOfQueryOnly(String str)198 public static Value valueOfQueryOnly(String str) { 199 return new Value(str, str.getBytes()); 200 } 201 valueOf(String str)202 public static Value valueOf(String str) { 203 validateValue(str); 204 return valueOfQueryOnly(str); 205 } 206 valueOf(byte[] bytes)207 public static Value valueOf(byte[] bytes) { 208 return new Value(new String(bytes), bytes); 209 } 210 compareTo(Value value)211 public int compareTo(Value value) { 212 return arrayCompare(getBytes(), value.getBytes()); 213 } 214 equals(Object o)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; toString(Value v)225 private static String toString(Value v) { 226 return v == null ? null : v.toString(); 227 } StringEnvironment(Map<Variable,Value> m)228 public StringEnvironment(Map<Variable,Value> m) {this.m = m;} size()229 public int size() {return m.size();} isEmpty()230 public boolean isEmpty() {return m.isEmpty();} clear()231 public void clear() { m.clear();} containsKey(Object key)232 public boolean containsKey(Object key) { 233 return m.containsKey(Variable.valueOfQueryOnly(key)); 234 } containsValue(Object value)235 public boolean containsValue(Object value) { 236 return m.containsValue(Value.valueOfQueryOnly(value)); 237 } get(Object key)238 public String get(Object key) { 239 return toString(m.get(Variable.valueOfQueryOnly(key))); 240 } put(String key, String value)241 public String put(String key, String value) { 242 return toString(m.put(Variable.valueOf(key), 243 Value.valueOf(value))); 244 } remove(Object key)245 public String remove(Object key) { 246 return toString(m.remove(Variable.valueOfQueryOnly(key))); 247 } keySet()248 public Set<String> keySet() { 249 return new StringKeySet(m.keySet()); 250 } entrySet()251 public Set<Map.Entry<String,String>> entrySet() { 252 return new StringEntrySet(m.entrySet()); 253 } values()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. toEnvironmentBlock(int[]envc)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 toEnvironmentBlock(Map<String,String> map, int[]envc)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; StringEntry(Map.Entry<Variable,Value> e)306 public StringEntry(Map.Entry<Variable,Value> e) {this.e = e;} getKey()307 public String getKey() {return e.getKey().toString();} getValue()308 public String getValue() {return e.getValue().toString();} setValue(String newValue)309 public String setValue(String newValue) { 310 return e.setValue(Value.valueOf(newValue)).toString(); 311 } toString()312 public String toString() {return getKey() + "=" + getValue();} equals(Object o)313 public boolean equals(Object o) { 314 return o instanceof StringEntry 315 && e.equals(((StringEntry)o).e); 316 } hashCode()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; StringEntrySet(Set<Map.Entry<Variable,Value>> s)324 public StringEntrySet(Set<Map.Entry<Variable,Value>> s) {this.s = s;} size()325 public int size() {return s.size();} isEmpty()326 public boolean isEmpty() {return s.isEmpty();} clear()327 public void clear() { s.clear();} iterator()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 } vvEntry(final Object o)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