Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions jdm-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,12 @@
<condition><not><isset property="smartctl.staged.ok"/></not></condition>
</fail>
<echo message="[smartctl] staged OK: ${project.build.directory}/smartctl/smartctl.exe"/>
<!-- Copy the static agent script alongside smartctl.exe.
jpackage bundles app/smartctl/ into Program Files (admin-write-only),
which is where resolveAgentScript() looks for it. -->
<copy file="${project.basedir}/src/main/resources/smartctl/smart-agent.ps1"
todir="${project.build.directory}/smartctl" overwrite="true"/>
<echo message="[smartctl] smart-agent.ps1 staged OK"/>
</target>
</configuration>
</execution>
Expand Down
47 changes: 29 additions & 18 deletions jdm-core/src/main/java/jdiskmark/Smart.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
Expand Down Expand Up @@ -356,41 +357,51 @@ public static void startHeartbeat() {
* @return a populated {@link Smart} instance, or {@code null} on error
*/
private static Smart getSmartDirect(String deviceName, String smartctlPath) {
List<List<String>> candidates = new ArrayList<>();
candidates.add(List.of("--json", "-a", "/dev/" + deviceName));
candidates.add(List.of("--json", "-a", deviceName));
if (deviceName.matches("^pd\\d+$")) {
String win32 = "\\\\.\\PhysicalDrive" + deviceName.substring(2);
candidates.add(List.of("--json", "-a", win32));
candidates.add(List.of("--json", "-a", win32, "-d", "nvme"));
candidates.add(List.of("--json", "-a", win32, "-d", "sat"));
}
Smart fallback = null;
try {
for (String devArg : new String[]{"/dev/" + deviceName, deviceName}) {
ProcessBuilder pb = new ProcessBuilder(smartctlPath, "--json", "-a", devArg);
for (List<String> args : candidates) {
List<String> cmd = new ArrayList<>();
cmd.add(smartctlPath);
cmd.addAll(args);
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process p = pb.start();

StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append('\n');
}
while ((line = reader.readLine()) != null) sb.append(line).append('\n');
}
if (!p.waitFor(15, TimeUnit.SECONDS)) {
p.destroyForcibly();
LOGGER.warning("getSmartDirect: smartctl timed out for: " + devArg);
App.err("Smart - smartctl timed out for: " + devArg);
LOGGER.warning("getSmartDirect: smartctl timed out for: " + args);
continue;
}
String result = sb.toString().trim();
if (result.isEmpty()) {
LOGGER.warning("getSmartDirect: empty response for: " + devArg);
App.err("Smart - empty response for: " + devArg);
continue;
}
if (!result.startsWith("{")) {
LOGGER.warning("getSmartDirect: non-JSON response for " + devArg + ": " + result);
App.err("Smart - non-JSON response for: " + devArg);
if (result.isEmpty() || !result.startsWith("{")) continue;
if ((p.exitValue() & 2) != 0) {
LOGGER.info("getSmartDirect: device open failed (exit " + p.exitValue() + ") for: " + args);
if (fallback == null) fallback = fromJson(result);
continue;
}
Smart smart = fromJson(result);
logSmart(smart);
return smart;
}
if (fallback != null) {
LOGGER.warning("getSmartDirect: all candidates failed; using error response for: " + deviceName);
logSmart(fallback);
return fallback;
}
LOGGER.severe("getSmartDirect: all attempts failed for: " + deviceName);
App.err("Smart - all attempts failed for: " + deviceName);
} catch (InterruptedException ex) {
Expand All @@ -399,7 +410,7 @@ private static Smart getSmartDirect(String deviceName, String smartctlPath) {
App.err("Smart - interrupted for: " + deviceName);
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "getSmartDirect failed for: " + deviceName, ex);
App.err("Smart - failed for: " + deviceName + " " + ex.getMessage());
App.err("Smart - failed for: " + deviceName + " \u2014 " + ex.getMessage());
}
return null;
}
Expand All @@ -413,7 +424,7 @@ private static Smart getSmartDirect(String deviceName, String smartctlPath) {
* {@code smartctl} directly via {@link #getSmartDirect}.</li>
* <li>Otherwise, delegates to {@link SmartEscalation#runElevated} which
* triggers a UAC prompt and runs an elevated helper, returning the
* JSON via a temp file in {@code %LOCALAPPDATA%\JDiskMark\}.</li>
* JSON via the version-scoped IPC directory ({@code ~/.jdm/<version>/smart-ipc/}).</li>
* </ul>
*
* <p>On <b>Linux / macOS</b>, writes the command to the persistent privileged
Expand Down
Loading
Loading