瀏覽代碼

license授权机制

zhanghaijun 2 年之前
父節點
當前提交
08115fb703

+ 9 - 0
train-server/pom.xml

@@ -54,10 +54,19 @@
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-json</artifactId>
         </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-crypto</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-cache</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+            <version>6.4.0</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 32 - 0
train-server/src/main/java/top/haijunit/train/controller/LicenseController.java

@@ -0,0 +1,32 @@
+package top.haijunit.train.controller;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import top.haijunit.common.domain.ResultData;
+import top.haijunit.train.service.LicenseService;
+
+/**
+ * @author zhanghaijun
+ * @date 2023/12/19 23:31
+ * @description 授权接口
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/license")
+public class LicenseController {
+
+    private final LicenseService licenseService;
+
+    @GetMapping("/code")
+    public ResultData<String> getMachineCode() {
+        return ResultData.success(licenseService.getMachineStr());
+    }
+
+    @PostMapping("/verify")
+    public ResultData<String> verifyLicense(String licenseStr) {
+        return licenseService.licenseVerify(licenseStr, licenseService.getMachineStr());
+    }
+
+}

+ 1 - 0
train-server/src/main/java/top/haijunit/train/domain/constant/CacheNames.java

@@ -16,5 +16,6 @@ package top.haijunit.train.domain.constant;
 public interface CacheNames {
 
     String SECTION = "section";
+    String LICENSE = "license";
 
 }

+ 4 - 4
train-server/src/main/java/top/haijunit/train/listener/ReportTimeHandle.java

@@ -33,7 +33,7 @@ public class ReportTimeHandle {
 
     /** 定时发送列车位置数据 */
     @Async
-    @Scheduled(fixedDelay = 1000)
+    @Scheduled(fixedDelay = 100)
     public void sendTrainPositionList() {
         for (TrainItem trainItem : TrainSimulationHolder.getTrainItem()) {
             TrainWebSocketUtils.sendMessage(CommandEnum.TRAIN, trainItem);
@@ -42,7 +42,7 @@ public class ReportTimeHandle {
 
     /** 定时发送进路占用数据 */
     @Async
-    @Scheduled(fixedDelay = 10000)
+    @Scheduled(fixedDelay = 100)
     public void sendRouterStatus() {
         Map<Long, LinkedList<SectionItem>> routeMap = TrainSimulationHolder.getRouteMap();
         TrainWebSocketUtils.sendMessage(CommandEnum.ROUTE_STATUS, new HashMap<>(routeMap));
@@ -50,7 +50,7 @@ public class ReportTimeHandle {
 
     /** 定时计算进路 */
     @Async
-    @Scheduled(fixedDelay = 1000)
+    @Scheduled(fixedDelay = 100)
     public void runRouteCompute() {
         for (TrainItem trainItem : TrainSimulationHolder.getTrainItem()) {
             routeComputerService.run(trainItem);
@@ -58,7 +58,7 @@ public class ReportTimeHandle {
     }
 
     @Async
-    @Scheduled(fixedDelay = 1000)
+    @Scheduled(fixedDelay = 100)
     public void runTrainCompute() {
         for (TrainItem trainItem : TrainSimulationHolder.getTrainItem()) {
             trainComputerService.run(trainItem);

+ 6 - 4
train-server/src/main/java/top/haijunit/train/service/DeviceService.java

@@ -34,13 +34,15 @@ public class DeviceService {
         if (!message.containsKey("cmd")) {
             throw new ServiceException(500, String.format("cmd指令解析失败,message:%s", msgStr));
         }
-        CommandEnum commandEnum = CommandEnum.getByCode(Convert.toInt(message.containsKey("cmd"), 0));
+        CommandEnum commandEnum = CommandEnum.getByCode(Convert.toInt(message.get("cmd"), 0));
         if (null != commandEnum && null != commandEnum.getClassType()) {
             try {
+                JSONObject data = JSONUtil.parseObj(message.get("data"));
                 SocketDevice.DeviceBase deviceBase = commandEnum.getClassType().getDeclaredConstructor().newInstance();
-                String code = Convert.toStr(message.get(deviceBase.getCodeName()), "");
-                String status = Convert.toStr(message.get(deviceBase.getStatusName()), "");
-                String other = Convert.toStr(message.get(deviceBase.getOtherName()), "");
+
+                String code = Convert.toStr(data.get(deviceBase.getCodeName()), "");
+                String status = Convert.toStr(data.get(deviceBase.getStatusName()), "");
+                String other = Convert.toStr(data.get(deviceBase.getOtherName()), "");
                 if (!StrUtil.isAllNotBlank(code, status)) {
                     throw new ServiceException(HttpStatus.HTTP_INTERNAL_ERROR, String.format("缺少字段: message:%s, code: %s, status: %s", msgStr, deviceBase.getCodeName(), deviceBase.getStatusName()));
                 }

+ 60 - 0
train-server/src/main/java/top/haijunit/train/service/LicenseService.java

@@ -0,0 +1,60 @@
+package top.haijunit.train.service;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import top.haijunit.common.domain.ResultData;
+import top.haijunit.train.domain.constant.CacheNames;
+import top.haijunit.train.utils.LicenseUtil;
+
+import java.util.List;
+
+/**
+ * @author zhanghaijun
+ * @date 2023/12/19 23:28
+ * @description 授权解析
+ */
+@Slf4j
+@Service
+public class LicenseService {
+
+
+    /**
+     * 获取机器码+校验位
+     * @return 机器码+校验位
+     */
+    @Cacheable(cacheNames = CacheNames.LICENSE, key = "'MachineStr'")
+    public String getMachineStr() {
+        String machineCode = getMachineCode();
+        return machineCode + LicenseUtil.MARK + LicenseUtil.generateCheckKey(machineCode);
+    }
+
+    @Cacheable(cacheNames = CacheNames.LICENSE, key = "'MachineCode'")
+    public String getMachineCode() {
+        return LicenseUtil.getMachineCode();
+    }
+
+    // 校验授权码
+    public ResultData<String> licenseVerify(String licenseStr, String machineStr) {
+        if (machineStr.contains(LicenseUtil.MARK)) {
+            String machineCode = StrUtil.subBefore(machineStr, LicenseUtil.MARK, false);
+            String code = StrUtil.subAfter(machineStr, LicenseUtil.MARK, false);
+            if (!StrUtil.equalsAnyIgnoreCase(code, LicenseUtil.generateCheckKey(machineCode))) {
+                return ResultData.fail("机器码错误", machineCode);
+            }
+            machineStr = machineCode;
+        }
+        List<String> list = StrUtil.split(LicenseUtil.licenseDecrypt(licenseStr), ",");
+        if (list.size() < 2) {
+            return ResultData.fail("授权码错误", machineStr);
+        }
+        if (!StrUtil.equalsAnyIgnoreCase(list.get(0), machineStr)) {
+            return ResultData.fail("机器码不匹配", machineStr);
+        }
+        if (Long.parseLong(list.get(1)) < System.currentTimeMillis()) {
+            return ResultData.fail("授权码已过期", machineStr);
+        }
+        return ResultData.success(machineStr);
+    }
+}

+ 84 - 0
train-server/src/main/java/top/haijunit/train/utils/LicenseUtil.java

@@ -0,0 +1,84 @@
+package top.haijunit.train.utils;
+
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.symmetric.AES;
+import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
+import cn.hutool.crypto.symmetric.SymmetricCrypto;
+import lombok.experimental.UtilityClass;
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.annotations.Cache;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.util.DigestUtils;
+import oshi.SystemInfo;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.HardwareAbstractionLayer;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+
+/**
+ * @author zhanghaijun
+ * @date 2023/12/19 21:40
+ * @description 授权工具类
+ */
+@UtilityClass
+public class LicenseUtil {
+
+    private static final byte[] key = "1234567890123456".getBytes();
+    public static final String MARK = "+";
+
+    /**
+     * license加密
+     *
+     * @param machineCode 机器码
+     * @return 密文
+     */
+    public String licenseEncrypt(String machineCode) {
+        AES aes = SecureUtil.aes(key);
+        return aes.encryptHex(machineCode);
+    }
+
+
+    /**
+     * license解密
+     *
+     * @param licenseStr 密文
+     * @return 明文
+     */
+    public String licenseDecrypt(String licenseStr) {
+        AES aes = SecureUtil.aes(key);
+        return aes.decryptStr(licenseStr, CharsetUtil.CHARSET_UTF_8);
+    }
+
+    public String getMachineCode() {
+        SystemInfo systemInfo = new SystemInfo();
+        HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
+        CentralProcessor processor = hardwareAbstractionLayer.getProcessor();
+        String serialNumber = hardwareAbstractionLayer.getComputerSystem().getSerialNumber();
+        String processorID = processor.getProcessorIdentifier().getProcessorID();
+        String machine = serialNumber + "_" + processorID;
+        return DigestUtils.md5DigestAsHex(StrUtil.toUnderlineCase(machine).getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * 获取校验位
+     *
+     * @param machineCode 机器码
+     * @return 校验位
+     */
+    public static String generateCheckKey(String machineCode) {
+        if (StringUtils.isBlank(machineCode)) {
+            return "000";
+        }
+        return String.format("%03d", Math.abs(machineCode.hashCode()) % 1000);
+    }
+
+
+}

+ 0 - 1
train-server/src/main/java/top/haijunit/train/utils/TrainWebSocketUtils.java

@@ -1,6 +1,5 @@
 package top.haijunit.train.utils;
 
-import cn.hutool.json.JSONUtil;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.experimental.UtilityClass;

+ 0 - 1
train-server/src/main/resources/application.yml

@@ -62,7 +62,6 @@ spring:
       ddl-auto: none
     show-sql: false
 
----
 websocket:
   enabled: true
   path: /websocket

+ 316 - 0
train-server/src/test/java/top/haijunit/train/test/LicenseTest.java

@@ -0,0 +1,316 @@
+package top.haijunit.train.test;
+
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import oshi.SystemInfo;
+import oshi.hardware.Baseboard;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.CentralProcessor.TickType;
+import oshi.hardware.ComputerSystem;
+import oshi.hardware.Display;
+import oshi.hardware.Firmware;
+import oshi.hardware.GlobalMemory;
+import oshi.hardware.HWDiskStore;
+import oshi.hardware.HWPartition;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.hardware.NetworkIF;
+import oshi.hardware.PowerSource;
+import oshi.hardware.Sensors;
+import oshi.hardware.UsbDevice;
+import oshi.software.os.FileSystem;
+import oshi.software.os.NetworkParams;
+import oshi.software.os.OSFileStore;
+import oshi.software.os.OSProcess;
+import oshi.software.os.OperatingSystem;
+import oshi.util.FormatUtil;
+import oshi.util.Util;
+import org.junit.jupiter.api.DisplayName;
+import top.haijunit.common.domain.ResultData;
+import top.haijunit.train.TrainServerMain;
+import top.haijunit.train.service.LicenseService;
+import top.haijunit.train.utils.LicenseUtil;
+
+/**
+ * @author zhanghaijun
+ * @date 2023/12/19 21:41
+ * @description 授权测试
+ */
+@Slf4j
+@DisplayName("授权测试")
+@SpringBootTest(classes = TrainServerMain.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class LicenseTest {
+
+    @Autowired
+    private LicenseService licenseService;
+
+    @DisplayName("授权+验证")
+    @Test
+    void licenseValid() {
+        String machineStr = licenseService.getMachineStr();
+        String dateStr = "2024-01-01";
+        String licenseStr = getLicenseStr(machineStr, dateStr);
+        log.info(">>>>>>授权码: 【{}】", licenseStr);
+        ResultData<String> result = licenseService.licenseVerify(licenseStr, machineStr);
+        log.info(">>>>>>验证结果: {}", result);
+    }
+
+    private String getLicenseStr(String machineStr, String dateStr) {
+        if (machineStr.contains(LicenseUtil.MARK)) {
+            String machineCode = StrUtil.subBefore(machineStr, LicenseUtil.MARK, false);
+            String code = StrUtil.subAfter(machineStr, LicenseUtil.MARK, false);
+            Assertions.assertEquals(code, LicenseUtil.generateCheckKey(machineCode));
+            machineStr = machineCode;
+        }
+        log.info(">>>>>>机器码: {}", machineStr);
+        String string = machineStr + "," + DateUtil.parse(dateStr).getTime();
+        String licenseStr = LicenseUtil.licenseEncrypt(string);
+        log.info(">>>>>>授权码: {}", licenseStr);
+        return licenseStr;
+    }
+
+
+    @Test
+    void getMachineCode() {
+        log.info("机器码: {}", LicenseUtil.getMachineCode());
+        log.info("校验位: {}", LicenseUtil.generateCheckKey(LicenseUtil.getMachineCode()));
+    }
+
+    @Test
+    void test() {
+        System.out.println("Initializing System...");
+        SystemInfo si = new SystemInfo();
+
+        HardwareAbstractionLayer hal = si.getHardware();
+        OperatingSystem os = si.getOperatingSystem();
+
+        System.out.println(os);
+
+        System.out.println("Checking computer system...");
+        printComputerSystem(hal.getComputerSystem());
+
+        System.out.println("Checking Processor...");
+        printProcessor(hal.getProcessor());
+
+        System.out.println("Checking Memory...");
+        printMemory(hal.getMemory());
+
+        System.out.println("Checking CPU...");
+        printCpu(hal.getProcessor());
+
+        System.out.println("Checking Processes...");
+        printProcesses(os, hal.getMemory());
+
+        System.out.println("Checking Sensors...");
+        printSensors(hal.getSensors());
+
+        System.out.println("Checking Power sources...");
+        printPowerSources(hal.getPowerSources());
+
+        System.out.println("Checking Disks...");
+        printDisks(hal.getDiskStores());
+
+        System.out.println("Checking File System...");
+        printFileSystem(os.getFileSystem());
+
+        System.out.println("Checking Network interfaces...");
+        printNetworkInterfaces(hal.getNetworkIFs());
+
+        System.out.println("Checking Network parameterss...");
+        printNetworkParameters(os.getNetworkParams());
+
+        // hardware: displays
+        System.out.println("Checking Displays...");
+        printDisplays(hal.getDisplays());
+
+        // hardware: USB devices
+        // System.out.println("Checking USB Devices...");
+        // printUsbDevices(hal.getUsbDevices(true));
+    }
+
+    private static void printComputerSystem(final ComputerSystem computerSystem) {
+
+        System.out.println("manufacturer: " + computerSystem.getManufacturer());
+        System.out.println("model: " + computerSystem.getModel());
+        System.out.println("serialnumber: " + computerSystem.getSerialNumber());
+        final Firmware firmware = computerSystem.getFirmware();
+        System.out.println("firmware:");
+        System.out.println("  manufacturer: " + firmware.getManufacturer());
+        System.out.println("  name: " + firmware.getName());
+        System.out.println("  description: " + firmware.getDescription());
+        System.out.println("  version: " + firmware.getVersion());
+//      System.out.println("  release date: " + (firmware.getReleaseDate() == null ? "unknown": firmware.getReleaseDate() == null ? "unknown" : FormatUtil.formatDate(firmware.getReleaseDate())));
+        final Baseboard baseboard = computerSystem.getBaseboard();
+        System.out.println("baseboard:");
+        System.out.println("  manufacturer: " + baseboard.getManufacturer());
+        System.out.println("  model: " + baseboard.getModel());
+        System.out.println("  version: " + baseboard.getVersion());
+        System.out.println("  serialnumber: " + baseboard.getSerialNumber());
+    }
+
+    private static void printProcessor(CentralProcessor processor) {
+        System.out.println(processor);
+        System.out.println(" " + processor.getPhysicalPackageCount() + " physical CPU package(s)");
+        System.out.println(" " + processor.getPhysicalProcessorCount() + " physical CPU core(s)");
+        System.out.println(" " + processor.getLogicalProcessorCount() + " logical CPU(s)");
+
+        System.out.println("Identifier: " + processor.getProcessorIdentifier());
+        System.out.println("ProcessorID: " + processor.getProcessorIdentifier().getProcessorID());
+    }
+
+    private static void printMemory(GlobalMemory memory) {
+        System.out.println("Memory: " + FormatUtil.formatBytes(memory.getAvailable()) + "/" + FormatUtil.formatBytes(memory.getTotal()));
+        System.out.println("Swap used: " + FormatUtil.formatBytes(memory.getVirtualMemory().getSwapUsed()) + "/" + FormatUtil.formatBytes(memory.getVirtualMemory().getSwapTotal()));
+    }
+
+    private static void printCpu(CentralProcessor processor) {
+        System.out.println("Context Switches/Interrupts: " + processor.getContextSwitches() + " / " + processor.getInterrupts());
+
+        long[] prevTicks = processor.getSystemCpuLoadTicks();
+        System.out.println("CPU, IOWait, and IRQ ticks @ 0 sec:" + Arrays.toString(prevTicks));
+        // Wait a second...
+        Util.sleep(1000);
+        long[] ticks = processor.getSystemCpuLoadTicks();
+        System.out.println("CPU, IOWait, and IRQ ticks @ 1 sec:" + Arrays.toString(ticks));
+        long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
+        long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
+        long sys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
+        long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
+        long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
+        long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
+        long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
+        long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
+        long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal;
+
+        System.out.format("User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%%n", 100d * user / totalCpu, 100d * nice / totalCpu, 100d * sys / totalCpu, 100d * idle / totalCpu, 100d * iowait / totalCpu, 100d * irq / totalCpu, 100d * softirq / totalCpu, 100d * steal / totalCpu);
+        System.out.format("CPU load: %.1f%% (counting ticks)%n", processor.getSystemCpuLoadBetweenTicks(prevTicks) * 100);
+        // System.out.format("CPU load: %.1f%% (OS MXBean)%n", processor.getSystemCpuLoad() * 100);
+        double[] loadAverage = processor.getSystemLoadAverage(3);
+        System.out.println("CPU load averages:" + (loadAverage[0] < 0 ? " N/A" : String.format(" %.2f", loadAverage[0])) + (loadAverage[1] < 0 ? " N/A" : String.format(" %.2f", loadAverage[1])) + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f", loadAverage[2])));
+        // per core CPU
+//      StringBuilder procCpu = new StringBuilder("CPU load per processor:");
+//      double[] load = processor.getProcessorCpuLoadBetweenTicks();
+//      for (double avg : load) {
+//          procCpu.append(String.format(" %.1f%%", avg * 100));
+//      }
+//      System.out.println(procCpu.toString());
+    }
+
+    private static void printProcesses(OperatingSystem os, GlobalMemory memory) {
+        System.out.println("Processes: " + os.getProcessCount() + ", Threads: " + os.getThreadCount());
+        // Sort by highest CPU
+        List<OSProcess> process = os.getProcesses();
+        System.out.println("   PID  %CPU %MEM       VSZ       RSS Name");
+        for (int i = 0; i < process.size() && i < 5; i++) {
+            OSProcess p = process.get(i);
+            System.out.format(" %5d %5.1f %4.1f %9s %9s %s%n", p.getProcessID(), 100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(), 100d * p.getResidentSetSize() / memory.getTotal(), FormatUtil.formatBytes(p.getVirtualSize()), FormatUtil.formatBytes(p.getResidentSetSize()), p.getName());
+        }
+    }
+
+    private static void printSensors(Sensors sensors) {
+        System.out.println("Sensors:");
+        System.out.format(" CPU Temperature: %.1f°C%n", sensors.getCpuTemperature());
+        System.out.println(" Fan Speeds: " + Arrays.toString(sensors.getFanSpeeds()));
+        System.out.format(" CPU Voltage: %.1fV%n", sensors.getCpuVoltage());
+    }
+
+    private static void printPowerSources(List<PowerSource> list) {
+        StringBuilder sb = new StringBuilder("Power: ");
+        if (list.isEmpty()) {
+            sb.append("Unknown");
+        } else {
+            double timeRemaining = list.get(0).getTimeRemainingInstant();
+            if (timeRemaining < -1d) {
+                sb.append("Charging");
+            } else if (timeRemaining < 0d) {
+                sb.append("Calculating time remaining");
+            } else {
+                sb.append(String.format("%d:%02d remaining", (int) (timeRemaining / 3600), (int) (timeRemaining / 60) % 60));
+            }
+        }
+        for (PowerSource pSource : list) {
+            sb.append(String.format("%n %s @ %.1f%%", pSource.getName(), pSource.getRemainingCapacityPercent() * 100d));
+        }
+        System.out.println(sb);
+    }
+
+    private static void printDisks(List<HWDiskStore> list) {
+        System.out.println("Disks:");
+        for (HWDiskStore disk : list) {
+            boolean readwrite = disk.getReads() > 0 || disk.getWrites() > 0;
+            System.out.format(" %s: (model: %s - S/N: %s) size: %s, reads: %s (%s), writes: %s (%s), xfer: %s ms%n", disk.getName(), disk.getModel(), disk.getSerial(), disk.getSize() > 0 ? FormatUtil.formatBytesDecimal(disk.getSize()) : "?", readwrite ? disk.getReads() : "?", readwrite ? FormatUtil.formatBytes(disk.getReadBytes()) : "?", readwrite ? disk.getWrites() : "?", readwrite ? FormatUtil.formatBytes(disk.getWriteBytes()) : "?", readwrite ? disk.getTransferTime() : "?");
+            List<HWPartition> partitions = disk.getPartitions();
+            if (partitions == null) {
+                // TODO Remove when all OS's implemented
+                continue;
+            }
+            for (HWPartition part : partitions) {
+                System.out.format(" |-- %s: %s (%s) Maj:Min=%d:%d, size: %s%s%n", part.getIdentification(), part.getName(), part.getType(), part.getMajor(), part.getMinor(), FormatUtil.formatBytesDecimal(part.getSize()), part.getMountPoint().isEmpty() ? "" : " @ " + part.getMountPoint());
+            }
+        }
+    }
+
+    private static void printFileSystem(FileSystem fileSystem) {
+        System.out.println("File System:");
+
+        System.out.format(" File Descriptors: %d/%d%n", fileSystem.getOpenFileDescriptors(), fileSystem.getMaxFileDescriptors());
+
+        List<OSFileStore> fsArray = fileSystem.getFileStores();
+        for (OSFileStore fs : fsArray) {
+            long usable = fs.getUsableSpace();
+            long total = fs.getTotalSpace();
+            System.out.format(" %s (%s) [%s] %s of %s free (%.1f%%) is %s " + (fs.getLogicalVolume() != null && fs.getLogicalVolume().length() > 0 ? "[%s]" : "%s") + " and is mounted at %s%n", fs.getName(), fs.getDescription().isEmpty() ? "file system" : fs.getDescription(), fs.getType(), FormatUtil.formatBytes(usable), FormatUtil.formatBytes(fs.getTotalSpace()), 100d * usable / total, fs.getVolume(), fs.getLogicalVolume(), fs.getMount());
+        }
+    }
+
+    private static void printNetworkInterfaces(List<NetworkIF> list) {
+        System.out.println("Network interfaces:");
+        for (NetworkIF net : list) {
+            System.out.format(" Name: %s (%s)%n", net.getName(), net.getDisplayName());
+            System.out.format("   MAC Address: %s %n", net.getMacaddr());
+            System.out.format("   MTU: %s, Speed: %s %n", net.getMTU(), FormatUtil.formatValue(net.getSpeed(), "bps"));
+            System.out.format("   IPv4: %s %n", Arrays.toString(net.getIPv4addr()));
+            System.out.format("   IPv6: %s %n", Arrays.toString(net.getIPv6addr()));
+            boolean hasData = net.getBytesRecv() > 0 || net.getBytesSent() > 0 || net.getPacketsRecv() > 0 || net.getPacketsSent() > 0;
+            System.out.format("   Traffic: received %s/%s%s; transmitted %s/%s%s %n", hasData ? net.getPacketsRecv() + " packets" : "?", hasData ? FormatUtil.formatBytes(net.getBytesRecv()) : "?", hasData ? " (" + net.getInErrors() + " err)" : "", hasData ? net.getPacketsSent() + " packets" : "?", hasData ? FormatUtil.formatBytes(net.getBytesSent()) : "?", hasData ? " (" + net.getOutErrors() + " err)" : "");
+        }
+    }
+
+    private static void printNetworkParameters(NetworkParams networkParams) {
+        System.out.println("Network parameters:");
+        System.out.format(" Host name: %s%n", networkParams.getHostName());
+        System.out.format(" Domain name: %s%n", networkParams.getDomainName());
+        System.out.format(" DNS servers: %s%n", Arrays.toString(networkParams.getDnsServers()));
+        System.out.format(" IPv4 Gateway: %s%n", networkParams.getIpv4DefaultGateway());
+        System.out.format(" IPv6 Gateway: %s%n", networkParams.getIpv6DefaultGateway());
+    }
+
+    private static void printDisplays(List<Display> list) {
+        System.out.println("Displays:");
+        int i = 0;
+        for (Display display : list) {
+            System.out.println(" Display " + i + ":");
+            System.out.println(display.toString());
+            i++;
+        }
+    }
+
+    private static void printUsbDevices(List<UsbDevice> list) {
+        System.out.println("USB Devices:");
+        for (UsbDevice usbDevice : list) {
+            System.out.println(usbDevice.toString());
+        }
+    }
+
+
+}