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