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