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