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