001package com.pi4j.system.impl;
002
003/*
004 * #%L
005 * **********************************************************************
006 * ORGANIZATION  :  Pi4J
007 * PROJECT       :  Pi4J :: Java Library (Core)
008 * FILENAME      :  DefaultSystemInfoProvider.java
009 *
010 * This file is part of the Pi4J project. More information about
011 * this project can be found here:  https://www.pi4j.com/
012 * **********************************************************************
013 * %%
014 * Copyright (C) 2012 - 2019 Pi4J
015 * %%
016 * This program is free software: you can redistribute it and/or modify
017 * it under the terms of the GNU Lesser General Public License as
018 * published by the Free Software Foundation, either version 3 of the
019 * License, or (at your option) any later version.
020 *
021 * This program is distributed in the hope that it will be useful,
022 * but WITHOUT ANY WARRANTY; without even the implied warranty of
023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
024 * GNU General Lesser Public License for more details.
025 *
026 * You should have received a copy of the GNU General Lesser Public
027 * License along with this program.  If not, see
028 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
029 * #L%
030 */
031
032import com.pi4j.system.SystemInfoProvider;
033import com.pi4j.util.ExecUtil;
034import com.pi4j.util.StringUtil;
035
036import java.io.BufferedReader;
037import java.io.FileReader;
038import java.io.IOException;
039import java.security.AccessController;
040import java.security.PrivilegedAction;
041import java.util.ArrayList;
042import java.util.HashMap;
043import java.util.List;
044import java.util.Map;
045
046/**
047 * Abstract base implementation of the SystemInfoProvider interface.
048 * This base impl includes support for all common method across all
049 * the supported platforms.
050 */
051public abstract class DefaultSystemInfoProvider extends SystemInfoProviderBase implements SystemInfoProvider {
052
053    private static Map<String, String> cpuInfo;
054
055    /**
056     * This method will read and parse the '/proc/cpuinfo' into a collection of properties.
057     *
058     * @param target
059     * @return
060     * @throws IOException
061     * @throws InterruptedException
062     * @throws UnsupportedOperationException
063     */
064    protected String getCpuInfo(String target) throws IOException, InterruptedException, UnsupportedOperationException {
065        // if the CPU data has not been previously acquired, then acquire it now
066        if (cpuInfo == null) {
067            cpuInfo = new HashMap<>();
068
069            try(BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"))) {
070                for(String line; (line = br.readLine()) != null; ) {
071                    String parts[] = line.split(":", 2);
072                    if (parts.length >= 2 && !parts[0].trim().isEmpty() && !parts[1].trim().isEmpty()) {
073                        String cpuKey = parts[0].trim();
074                        cpuInfo.put(cpuKey, parts[1].trim());
075                    }
076                }
077            }
078        }
079        if (cpuInfo.containsKey(target)) {
080            return cpuInfo.get(target);
081        }
082        throw new UnsupportedOperationException("Invalid target: " + target);
083    }
084
085    /*
086     * this method will to obtain the version info string from the 'bash' program
087     * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system)
088     */
089    protected String getBashVersionInfo() {
090        String versionInfo = "";
091        try {
092            String result[] = ExecUtil.execute("bash --version");
093            for(String line : result) {
094                if(!line.isEmpty()) {
095                    versionInfo = line; // return only first output line of version info
096                    break;
097                }
098            }
099        }
100        catch (IOException|InterruptedException ioe) { ioe.printStackTrace(); }
101        return versionInfo;
102    }
103
104    /*
105     * this method will determine if a specified tag exists from the elf info in the '/proc/self/exe' program
106     * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system)
107     */
108    protected boolean hasReadElfTag(String tag) {
109        String tagValue = getReadElfTag(tag);
110        return (tagValue != null && !tagValue.isEmpty());
111    }
112
113    /*
114     * this method will obtain a specified tag value from the elf info in the '/proc/self/exe' program
115     * (this method is used to help determine the HARD-FLOAT / SOFT-FLOAT ABI of the system)
116     */
117    protected String getReadElfTag(String tag) {
118        String tagValue = null;
119        try {
120            String result[] = ExecUtil.execute("/usr/bin/readelf -A /proc/self/exe");
121            if(result != null){
122                for(String line : result) {
123                    line = line.trim();
124                    if (line.startsWith(tag) && line.contains(":")) {
125                        String lineParts[] = line.split(":", 2);
126                        if(lineParts.length > 1)
127                            tagValue = lineParts[1].trim();
128                        break;
129                    }
130                }
131            }
132        }
133        catch (IOException|InterruptedException ioe) { ioe.printStackTrace(); }
134        return tagValue;
135    }
136
137    protected List<Long> getMemory() throws IOException, InterruptedException {
138        // Memory information is in the form
139        // root@mypi:/home/pi# free -b
140        //              total       used       free     shared    buffers     cached
141        // Mem:     459771904  144654336  315117568          0   21319680   63713280
142        // -/+ buffers/cache:   59621376  400150528
143        // Swap:    104853504          0  104853504
144        List<Long> values = new ArrayList<>();
145        String result[] = ExecUtil.execute("free -b");
146        if(result != null){
147            for(String line : result) {
148                if(line.startsWith("Mem:")){
149                    String parts[] = line.split(" ");
150                    for(String part : parts){
151                        part = part.trim();
152                        if(!part.isEmpty() && !part.equalsIgnoreCase("Mem:")){
153                            values.add(new Long(part));
154                        }
155                    }
156                }
157            }
158        }
159        return values;
160    }
161
162    @Override
163    public String getProcessor() throws IOException, InterruptedException, UnsupportedOperationException {
164        return getCpuInfo("processor");
165    }
166
167    @Override
168    public String getBogoMIPS() throws IOException, InterruptedException, UnsupportedOperationException {
169        return getCpuInfo("BogoMIPS");
170    }
171
172    @Override
173    public String[] getCpuFeatures() throws IOException, InterruptedException, UnsupportedOperationException {
174        return getCpuInfo("Features").split(" ");
175    }
176
177    @Override
178    public String getCpuImplementer() throws IOException, InterruptedException, UnsupportedOperationException {
179        return getCpuInfo("CPU implementer");
180    }
181
182    @Override
183    public String getCpuArchitecture() throws IOException, InterruptedException, UnsupportedOperationException {
184        return getCpuInfo("CPU architecture");
185    }
186
187    @Override
188    public String getCpuVariant() throws IOException, InterruptedException, UnsupportedOperationException {
189        return getCpuInfo("CPU variant");
190    }
191
192    @Override
193    public String getCpuPart() throws IOException, InterruptedException, UnsupportedOperationException {
194        return getCpuInfo("CPU part");
195    }
196
197    @Override
198    public String getCpuRevision() throws IOException, InterruptedException, UnsupportedOperationException {
199        return getCpuInfo("CPU revision");
200    }
201
202    @Override
203    public String getHardware() throws IOException, InterruptedException, UnsupportedOperationException {
204        return getCpuInfo("Hardware");
205    }
206
207    @Override
208    public String getRevision() throws IOException, InterruptedException, UnsupportedOperationException {
209        return getCpuInfo("Revision");
210    }
211
212    @Override
213    public String getSerial() throws IOException, InterruptedException, UnsupportedOperationException {
214        return getCpuInfo("Serial");
215    }
216
217    @Override
218    public String getOsName() throws UnsupportedOperationException {
219        return System.getProperty("os.name");
220    }
221
222    @Override
223    public String getOsVersion() throws UnsupportedOperationException {
224        return System.getProperty("os.version");
225    }
226
227    @Override
228    public String getOsArch() throws UnsupportedOperationException {
229        return System.getProperty("os.arch");
230    }
231
232    @Override
233    public String getJavaVendor() throws UnsupportedOperationException {
234        return System.getProperty("java.vendor");
235    }
236
237    @Override
238    public String getJavaVendorUrl() throws UnsupportedOperationException {
239        return System.getProperty("java.vendor.url");
240    }
241
242    @Override
243    public String getJavaVersion() throws UnsupportedOperationException {
244        return System.getProperty("java.version");
245    }
246
247    @Override
248    public String getJavaVirtualMachine() throws UnsupportedOperationException {
249        return System.getProperty("java.vm.name");
250    }
251
252    @Override
253    public String getJavaRuntime() throws UnsupportedOperationException {
254        return AccessController.doPrivileged(new PrivilegedAction<String>() {
255            public String run() {
256                return System.getProperty("java.runtime.name");
257            }
258        });
259    }
260
261    /*
262     * this method was partially derived from :: (project) jogamp / (developer) sgothel
263     * https://github.com/sgothel/gluegen/blob/master/src/java/jogamp/common/os/PlatformPropsImpl.java#L160
264     * https://github.com/sgothel/gluegen/blob/master/LICENSE.txt
265     */
266    @Override
267    public boolean isHardFloatAbi() throws UnsupportedOperationException {
268        return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
269            private final String[] gnueabihf = new String[] { "gnueabihf", "armhf" };
270            public Boolean run() {
271                return ( StringUtil.contains(System.getProperty("sun.boot.library.path"), gnueabihf) ||
272                        StringUtil.contains(System.getProperty("java.library.path"), gnueabihf) ||
273                        StringUtil.contains(System.getProperty("java.home"), gnueabihf) ||
274                        getBashVersionInfo().contains("gnueabihf") ||
275                        hasReadElfTag("Tag_ABI_HardFP_use"));
276            } } );
277    }
278
279    @Override
280    public long getMemoryTotal() throws IOException, InterruptedException, UnsupportedOperationException {
281        List<Long> values = getMemory();
282        if(!values.isEmpty() && values.size() > 0){
283            return values.get(0); // total memory value is in first position
284        }
285        throw new UnsupportedOperationException();
286    }
287
288    @Override
289    public long getMemoryUsed() throws IOException, InterruptedException, UnsupportedOperationException {
290        List<Long> values = getMemory();
291        if(!values.isEmpty() && values.size() > 1){
292            return values.get(1); // used memory value is in second position
293        }
294        throw new UnsupportedOperationException();
295    }
296
297    @Override
298    public long getMemoryFree() throws IOException, InterruptedException, UnsupportedOperationException {
299        List<Long> values = getMemory();
300        if(!values.isEmpty() && values.size() > 2){
301            return values.get(2); // free memory value is in third position
302        }
303        throw new UnsupportedOperationException();
304    }
305
306    @Override
307    public long getMemoryShared() throws IOException, InterruptedException, UnsupportedOperationException {
308        List<Long> values = getMemory();
309        if(!values.isEmpty() && values.size() > 3){
310            return values.get(3); // shared memory value is in fourth position
311        }
312        throw new UnsupportedOperationException();
313    }
314
315    @Override
316    public long getMemoryBuffers() throws IOException, InterruptedException, UnsupportedOperationException {
317        List<Long> values = getMemory();
318        if(!values.isEmpty() && values.size() > 4){
319            return values.get(4); // buffers memory value is in fifth position
320        }
321        throw new UnsupportedOperationException();
322    }
323
324    @Override
325    public long getMemoryCached() throws IOException, InterruptedException, UnsupportedOperationException {
326        List<Long> values = getMemory();
327        if(!values.isEmpty() && values.size() > 5){
328            return values.get(5); // cached memory value is in sixth position
329        }
330        throw new UnsupportedOperationException();
331    }
332
333/**
334 *  The following commented out methods are implemented in the platform specific providers, if supported
335 */
336//    public String getModelName() throws IOException, InterruptedException, UnsupportedOperationException {
337//    public static String getOsFirmwareBuild() throws IOException, InterruptedException {
338//    public String getOsFirmwareDate() throws IOException, InterruptedException, ParseException, UnsupportedOperationException {
339//    public SystemInfo.BoardType getBoardType() throws IOException, InterruptedException, UnsupportedOperationException {
340//    public float getCpuTemperature() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
341//    public float getCpuVoltage() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
342//    public float getMemoryVoltageSDRam_C() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
343//    public float getMemoryVoltageSDRam_I() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
344//    public float getMemoryVoltageSDRam_P() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
345//    public boolean getCodecH264Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
346//    public boolean getCodecMPG2Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
347//    public boolean getCodecWVC1Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
348//    public long getClockFrequencyArm() throws IOException, InterruptedException, UnsupportedOperationException {
349//    public long getClockFrequencyCore() throws IOException, InterruptedException, UnsupportedOperationException {
350//    public long getClockFrequencyH264() throws IOException, InterruptedException, UnsupportedOperationException {
351//    public long getClockFrequencyISP() throws IOException, InterruptedException, UnsupportedOperationException {
352//    public long getClockFrequencyV3D() throws IOException, InterruptedException, UnsupportedOperationException {
353//    public long getClockFrequencyUART() throws IOException, InterruptedException, UnsupportedOperationException {
354//    public long getClockFrequencyPWM() throws IOException, InterruptedException, UnsupportedOperationException {
355//    public long getClockFrequencyEMMC() throws IOException, InterruptedException, UnsupportedOperationException {
356//    public long getClockFrequencyPixel() throws IOException, InterruptedException, UnsupportedOperationException {
357//    public long getClockFrequencyVEC() throws IOException, InterruptedException, UnsupportedOperationException {
358//    public long getClockFrequencyHDMI() throws IOException, InterruptedException, UnsupportedOperationException {
359//    public long getClockFrequencyDPI() throws IOException, InterruptedException, UnsupportedOperationException {
360}