1 /*
   2  * Copyright (c) 2018, 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.  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 package jdk.internal.platform.cgroupv1;
  27 
  28 import java.io.BufferedReader;
  29 import java.io.IOException;
  30 import java.math.BigInteger;
  31 import java.nio.file.Files;
  32 import java.nio.file.Paths;
  33 import java.security.AccessController;
  34 import java.security.PrivilegedActionException;
  35 import java.security.PrivilegedExceptionAction;
  36 import java.util.ArrayList;
  37 import java.util.List;
  38 import java.util.Optional;
  39 import java.util.function.Function;
  40 import java.util.stream.Stream;
  41 
  42 public class SubSystem {
  43     String root;
  44     String mountPoint;
  45     String path;
  46 
  47     public SubSystem(String root, String mountPoint) {
  48         this.root = root;
  49         this.mountPoint = mountPoint;
  50     }
  51 
  52     public void setPath(String cgroupPath) {
  53         if (root != null && cgroupPath != null) {
  54             if (root.equals("/")) {
  55                 if (!cgroupPath.equals("/")) {
  56                     path = mountPoint + cgroupPath;
  57                 }
  58                 else {
  59                     path = mountPoint;
  60                 }
  61             }
  62             else {
  63                 if (root.equals(cgroupPath)) {
  64                     path = mountPoint;
  65                 }
  66                 else {
  67                     if (cgroupPath.startsWith(root)) {
  68                         if (cgroupPath.length() > root.length()) {
  69                             String cgroupSubstr = cgroupPath.substring(root.length());
  70                             path = mountPoint + cgroupSubstr;
  71                         }
  72                     }
  73                 }
  74             }
  75         }
  76     }
  77 
  78     public String path() {
  79         return path;
  80     }
  81 
  82     /**
  83      * getSubSystemStringValue
  84      *
  85      * Return the first line of the file "parm" argument from the subsystem.
  86      *
  87      * TODO:  Consider using weak references for caching BufferedReader object.
  88      *
  89      * @param subsystem
  90      * @param parm
  91      * @return Returns the contents of the file specified by param.
  92      */
  93     public static String getStringValue(SubSystem subsystem, String parm) {
  94         if (subsystem == null) return null;
  95 
  96         try {
  97             return subsystem.readStringValue(parm);
  98         } catch (IOException e) {
  99             return null;
 100         }
 101     }
 102 
 103     private String readStringValue(String param) throws IOException {
 104         PrivilegedExceptionAction<BufferedReader> pea = () ->
 105                 Files.newBufferedReader(Paths.get(path(), param));
 106         try (BufferedReader bufferedReader =
 107                      AccessController.doPrivileged(pea)) {
 108             String line = bufferedReader.readLine();
 109             return line;
 110         } catch (PrivilegedActionException e) {
 111             Metrics.unwrapIOExceptionAndRethrow(e);
 112             throw new InternalError(e.getCause());
 113         }
 114     }
 115 
 116     public static long getLongValueMatchingLine(SubSystem subsystem,
 117                                                      String param,
 118                                                      String match,
 119                                                      Function<String, Long> conversion) {
 120         long retval = Metrics.unlimited_minimum + 1; // default unlimited
 121         try {
 122             List<String> lines = subsystem.readMatchingLines(param);
 123             for (String line : lines) {
 124                 if (line.startsWith(match)) {
 125                     retval = conversion.apply(line);
 126                     break;
 127                 }
 128             }
 129         } catch (IOException e) {
 130             // Ignore. Default is unlimited.
 131         }
 132         return retval;
 133     }
 134 
 135     private List<String> readMatchingLines(String param) throws IOException {
 136         try {
 137             PrivilegedExceptionAction<List<String>> pea = () ->
 138                     Files.readAllLines(Paths.get(path(), param));
 139             return AccessController.doPrivileged(pea);
 140         } catch (PrivilegedActionException e) {
 141             Metrics.unwrapIOExceptionAndRethrow(e);
 142             throw new InternalError(e.getCause());
 143         }
 144     }
 145 
 146     public static long getLongValue(SubSystem subsystem, String parm) {
 147         String strval = getStringValue(subsystem, parm);
 148         return convertStringToLong(strval);
 149     }
 150 
 151     public static long convertStringToLong(String strval) {
 152         long retval = 0;
 153         if (strval == null) return 0L;
 154 
 155         try {
 156             retval = Long.parseLong(strval);
 157         } catch (NumberFormatException e) {
 158             // For some properties (e.g. memory.limit_in_bytes) we may overflow the range of signed long.
 159             // In this case, return Long.MAX_VALUE
 160             BigInteger b = new BigInteger(strval);
 161             if (b.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
 162                 return Long.MAX_VALUE;
 163             }
 164         }
 165         return retval;
 166     }
 167 
 168     public static double getDoubleValue(SubSystem subsystem, String parm) {
 169         String strval = getStringValue(subsystem, parm);
 170 
 171         if (strval == null) return 0L;
 172 
 173         double retval = Double.parseDouble(strval);
 174 
 175         return retval;
 176     }
 177 
 178     /**
 179      * getSubSystemlongEntry
 180      *
 181      * Return the long value from the line containing the string "entryname"
 182      * within file "parm" in the "subsystem".
 183      *
 184      * TODO:  Consider using weak references for caching BufferedReader object.
 185      *
 186      * @param subsystem
 187      * @param parm
 188      * @param entryname
 189      * @return long value
 190      */
 191     public static long getLongEntry(SubSystem subsystem, String parm, String entryname) {
 192         String val = null;
 193 
 194         if (subsystem == null) return 0L;
 195 
 196         try (Stream<String> lines = Metrics.readFilePrivileged(Paths.get(subsystem.path(), parm))) {
 197 
 198             Optional<String> result = lines.map(line -> line.split(" "))
 199                                            .filter(line -> (line.length == 2 &&
 200                                                    line[0].equals(entryname)))
 201                                            .map(line -> line[1])
 202                                            .findFirst();
 203 
 204             return result.isPresent() ? Long.parseLong(result.get()) : 0L;
 205         }
 206         catch (IOException e) {
 207             return 0L;
 208         }
 209     }
 210 
 211     public static int getIntValue(SubSystem subsystem, String parm) {
 212         String val = getStringValue(subsystem, parm);
 213 
 214         if (val == null) return 0;
 215 
 216         return Integer.parseInt(val);
 217     }
 218 
 219     /**
 220      * StringRangeToIntArray
 221      *
 222      * Convert a string in the form of  1,3-4,6 to an array of
 223      * integers containing all the numbers in the range.
 224      *
 225      * @param range
 226      * @return int[] containing a sorted list of processors or memory nodes
 227      */
 228     public static int[] StringRangeToIntArray(String range) {
 229         int[] ints = new int[0];
 230 
 231         if (range == null) return ints;
 232 
 233         ArrayList<Integer> results = new ArrayList<>();
 234         String strs[] = range.split(",");
 235         for (String str : strs) {
 236             if (str.contains("-")) {
 237                 String lohi[] = str.split("-");
 238                 // validate format
 239                 if (lohi.length != 2) {
 240                     continue;
 241                 }
 242                 int lo = Integer.parseInt(lohi[0]);
 243                 int hi = Integer.parseInt(lohi[1]);
 244                 for (int i = lo; i <= hi; i++) {
 245                     results.add(i);
 246                 }
 247             }
 248             else {
 249                 results.add(Integer.parseInt(str));
 250             }
 251         }
 252 
 253         // sort results
 254         results.sort(null);
 255 
 256         // convert ArrayList to primitive int array
 257         ints = new int[results.size()];
 258         int i = 0;
 259         for (Integer n : results) {
 260             ints[i++] = n;
 261         }
 262 
 263         return ints;
 264     }
 265 
 266     public static class MemorySubSystem extends SubSystem {
 267 
 268         private boolean hierarchical;
 269 
 270         public MemorySubSystem(String root, String mountPoint) {
 271             super(root, mountPoint);
 272         }
 273 
 274         boolean isHierarchical() {
 275             return hierarchical;
 276         }
 277 
 278         void setHierarchical(boolean hierarchical) {
 279             this.hierarchical = hierarchical;
 280         }
 281 
 282     }
 283 }