1 /*
   2  * Copyright (c) 2013, 2019, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package jdk.test.lib;
  25 
  26 import java.io.File;
  27 import java.io.IOException;
  28 import java.io.RandomAccessFile;
  29 import java.util.regex.Pattern;
  30 import java.security.AccessController;
  31 import java.security.PrivilegedAction;
  32 import java.security.PrivilegedActionException;
  33 import java.security.PrivilegedExceptionAction;
  34 
  35 public class Platform {
  36 
  37     public static class Docker {
  38         // Use this property to specify docker location on your system.
  39         // E.g.: "/usr/local/bin/docker". We define this constant here so
  40         // that it can be used in VMProps as well which checks docker support
  41         // via this command
  42         public static final String DOCKER_COMMAND =
  43             System.getProperty("jdk.test.docker.command", "docker");
  44     }
  45     public  static final String vmName      = privilegedGetProperty("java.vm.name");
  46     public  static final String vmInfo      = privilegedGetProperty("java.vm.info");
  47     private static final String osVersion   = privilegedGetProperty("os.version");
  48     private static       int osVersionMajor = -1;
  49     private static       int osVersionMinor = -1;
  50     private static final String osName      = privilegedGetProperty("os.name");
  51     private static final String dataModel   = privilegedGetProperty("sun.arch.data.model");
  52     private static final String vmVersion   = privilegedGetProperty("java.vm.version");
  53     private static final String jdkDebug    = privilegedGetProperty("jdk.debug");
  54     private static final String osArch      = privilegedGetProperty("os.arch");
  55     private static final String userName    = privilegedGetProperty("user.name");
  56     private static final String compiler    = privilegedGetProperty("sun.management.compiler");
  57 
  58     private static String privilegedGetProperty(String key) {
  59         return AccessController.doPrivileged((
  60                 PrivilegedAction<String>) () -> System.getProperty(key));
  61     }
  62 
  63     public static boolean isClient() {
  64         return vmName.endsWith(" Client VM");
  65     }
  66 
  67     public static boolean isServer() {
  68         return vmName.endsWith(" Server VM");
  69     }
  70 
  71     public static boolean isGraal() {
  72         return vmName.endsWith(" Graal VM");
  73     }
  74 
  75     public static boolean isZero() {
  76         return vmName.endsWith(" Zero VM");
  77     }
  78 
  79     public static boolean isMinimal() {
  80         return vmName.endsWith(" Minimal VM");
  81     }
  82 
  83     public static boolean isEmbedded() {
  84         return vmName.contains("Embedded");
  85     }
  86 
  87     public static boolean isEmulatedClient() {
  88         return vmInfo.contains(" emulated-client");
  89     }
  90 
  91     public static boolean isTieredSupported() {
  92         return compiler.contains("Tiered Compilers");
  93     }
  94 
  95     public static boolean isInt() {
  96         return vmInfo.contains("interpreted");
  97     }
  98 
  99     public static boolean isMixed() {
 100         return vmInfo.contains("mixed");
 101     }
 102 
 103     public static boolean isComp() {
 104         return vmInfo.contains("compiled");
 105     }
 106 
 107     public static boolean is32bit() {
 108         return dataModel.equals("32");
 109     }
 110 
 111     public static boolean is64bit() {
 112         return dataModel.equals("64");
 113     }
 114 
 115     public static boolean isAix() {
 116         return isOs("aix");
 117     }
 118 
 119     public static boolean isLinux() {
 120         return isOs("linux");
 121     }
 122 
 123     public static boolean isOSX() {
 124         return isOs("mac");
 125     }
 126 
 127     public static boolean isSolaris() {
 128         return isOs("sunos");
 129     }
 130 
 131     public static boolean isWindows() {
 132         return isOs("win");
 133     }
 134 
 135     private static boolean isOs(String osname) {
 136         return osName.toLowerCase().startsWith(osname.toLowerCase());
 137     }
 138 
 139     public static String getOsName() {
 140         return osName;
 141     }
 142 
 143     // Os version support.
 144     private static void init_version() {
 145         String[] osVersionTokens = osVersion.split("\\.");
 146         try {
 147             if (osVersionTokens.length > 0) {
 148                 osVersionMajor = Integer.parseInt(osVersionTokens[0]);
 149                 if (osVersionTokens.length > 1) {
 150                     osVersionMinor = Integer.parseInt(osVersionTokens[1]);
 151                 }
 152             }
 153         } catch (NumberFormatException e) {
 154             osVersionMajor = osVersionMinor = 0;
 155         }
 156     }
 157 
 158     public static String getOsVersion() {
 159         return osVersion;
 160     }
 161 
 162     // Returns major version number from os.version system property.
 163     // E.g. 5 on Solaris 10 and 3 on SLES 11.3 (for the linux kernel version).
 164     public static int getOsVersionMajor() {
 165         if (osVersionMajor == -1) init_version();
 166         return osVersionMajor;
 167     }
 168 
 169     // Returns minor version number from os.version system property.
 170     // E.g. 10 on Solaris 10 and 0 on SLES 11.3 (for the linux kernel version).
 171     public static int getOsVersionMinor() {
 172         if (osVersionMinor == -1) init_version();
 173         return osVersionMinor;
 174     }
 175 
 176     public static boolean isDebugBuild() {
 177         return (jdkDebug.toLowerCase().contains("debug"));
 178     }
 179 
 180     public static boolean isSlowDebugBuild() {
 181         return (jdkDebug.toLowerCase().equals("slowdebug"));
 182     }
 183 
 184     public static boolean isFastDebugBuild() {
 185         return (jdkDebug.toLowerCase().equals("fastdebug"));
 186     }
 187 
 188     public static String getVMVersion() {
 189         return vmVersion;
 190     }
 191 
 192     public static boolean isAArch64() {
 193         return isArch("aarch64");
 194     }
 195 
 196     public static boolean isARM() {
 197         return isArch("arm.*");
 198     }
 199 
 200     public static boolean isPPC() {
 201         return isArch("ppc.*");
 202     }
 203 
 204     // Returns true for IBM z System running linux.
 205     public static boolean isS390x() {
 206         return isArch("s390.*") || isArch("s/390.*") || isArch("zArch_64");
 207     }
 208 
 209     // Returns true for sparc and sparcv9.
 210     public static boolean isSparc() {
 211         return isArch("sparc.*");
 212     }
 213 
 214     public static boolean isX64() {
 215         // On OSX it's 'x86_64' and on other (Linux, Windows and Solaris) platforms it's 'amd64'
 216         return isArch("(amd64)|(x86_64)");
 217     }
 218 
 219     public static boolean isX86() {
 220         // On Linux it's 'i386', Windows 'x86' without '_64' suffix.
 221         return isArch("(i386)|(x86(?!_64))");
 222     }
 223 
 224     public static String getOsArch() {
 225         return osArch;
 226     }
 227 
 228     /**
 229      * Return a boolean for whether SA and jhsdb are ported/available
 230      * on this platform.
 231      */
 232     public static boolean hasSA() {
 233         if (isAix()) {
 234             return false; // SA not implemented.
 235         } else if (isLinux()) {
 236             if (isS390x() || isARM()) {
 237                 return false; // SA not implemented.
 238             }
 239         }
 240         // Other platforms expected to work:
 241         return true;
 242     }
 243 
 244     /**
 245      * Return a boolean for whether we expect to be able to attach
 246      * the SA to our own processes on this system.  This requires
 247      * that SA is ported/available on this platform.
 248      */
 249     public static boolean shouldSAAttach() throws IOException {
 250         if (!hasSA()) return false;
 251         if (isLinux()) {
 252             return canPtraceAttachLinux();
 253         } else if (isOSX()) {
 254             return canAttachOSX();
 255         } else {
 256             // Other platforms expected to work:
 257             return true;
 258         }
 259     }
 260 
 261     /**
 262      * On Linux, first check the SELinux boolean "deny_ptrace" and return false
 263      * as we expect to be denied if that is "1".  Then expect permission to attach
 264      * if we are root, so return true.  Then return false for an expected denial
 265      * if "ptrace_scope" is 1, and true otherwise.
 266      */
 267     private static boolean canPtraceAttachLinux() throws IOException {
 268         // SELinux deny_ptrace:
 269         File deny_ptrace = new File("/sys/fs/selinux/booleans/deny_ptrace");
 270         if (deny_ptrace.exists()) {
 271             try (RandomAccessFile file = AccessController.doPrivileged(
 272                     (PrivilegedExceptionAction<RandomAccessFile>) () -> new RandomAccessFile(deny_ptrace, "r"))) {
 273                 if (file.readByte() != '0') {
 274                     return false;
 275                 }
 276             } catch (PrivilegedActionException e) {
 277                 @SuppressWarnings("unchecked")
 278                 IOException t = (IOException) e.getException();
 279                 throw t;
 280             }
 281         }
 282 
 283         // YAMA enhanced security ptrace_scope:
 284         // 0 - a process can PTRACE_ATTACH to any other process running under the same uid
 285         // 1 - restricted ptrace: a process must be a children of the inferior or user is root
 286         // 2 - only processes with CAP_SYS_PTRACE may use ptrace or user is root
 287         // 3 - no attach: no processes may use ptrace with PTRACE_ATTACH
 288         File ptrace_scope = new File("/proc/sys/kernel/yama/ptrace_scope");
 289         if (ptrace_scope.exists()) {
 290             try (RandomAccessFile file = AccessController.doPrivileged(
 291                     (PrivilegedExceptionAction<RandomAccessFile>) () -> new RandomAccessFile(ptrace_scope, "r"))) {
 292                 byte yama_scope = file.readByte();
 293                 if (yama_scope == '3') {
 294                     return false;
 295                 }
 296 
 297                 if (!userName.equals("root") && yama_scope != '0') {
 298                     return false;
 299                 }
 300             } catch (PrivilegedActionException e) {
 301                 @SuppressWarnings("unchecked")
 302                 IOException t = (IOException) e.getException();
 303                 throw t;
 304             }
 305         }
 306         // Otherwise expect to be permitted:
 307         return true;
 308     }
 309 
 310     /**
 311      * On OSX, expect permission to attach only if we are root.
 312      */
 313     private static boolean canAttachOSX() {
 314         return userName.equals("root");
 315     }
 316 
 317     private static boolean isArch(String archnameRE) {
 318         return Pattern.compile(archnameRE, Pattern.CASE_INSENSITIVE)
 319                       .matcher(osArch)
 320                       .matches();
 321     }
 322 
 323     /**
 324      * Returns file extension of shared library, e.g. "so" on linux, "dll" on windows.
 325      * @return file extension
 326      */
 327     public static String sharedLibraryExt() {
 328         if (isWindows()) {
 329             return "dll";
 330         } else if (isOSX()) {
 331             return "dylib";
 332         } else {
 333             return "so";
 334         }
 335     }
 336 
 337     /*
 338      * Returns name of system variable containing paths to shared native libraries.
 339      */
 340     public static String sharedLibraryPathVariableName() {
 341         if (isWindows()) {
 342             return "PATH";
 343         } else if (isOSX()) {
 344             return "DYLD_LIBRARY_PATH";
 345         } else if (isAix()) {
 346             return "LIBPATH";
 347         } else {
 348             return "LD_LIBRARY_PATH";
 349         }
 350     }
 351 
 352     public static boolean isDefaultCDSArchiveSupported() {
 353         return (is64bit()  &&
 354                 isServer() &&
 355                 (isLinux()   ||
 356                  isOSX()     ||
 357                  isSolaris() ||
 358                  isWindows()) &&
 359                 !isZero()    &&
 360                 !isMinimal() &&
 361                 !isAArch64() &&
 362                 !isARM());
 363     }
 364 
 365     /*
 366      * This should match the #if condition in ClassListParser::load_class_from_source().
 367      */
 368     public static boolean areCustomLoadersSupportedForCDS() {
 369         return (is64bit() && (isLinux() || isSolaris() || isOSX()));
 370     }
 371 }