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