Skip to content

Commit 564f044

Browse files
committed
feat(install): auto-discover target disk and run CopyFiles before GRUB
Revisit the command-line installer so a headless install needs no interactive device entry: - Resolve the target device from a CLI arg, an auto-discovered single JFAT mount, or an interactive prompt fallback, and feed it to the actions via the new ActionConstants.DEVICE_ID context value. - GrubInstallerAction reads DEVICE_ID from the context (prompting only when absent) instead of always asking, so the install runs non-interactively once the device is known. - Run CopyFilesAction before GrubInstallerAction so stage1.5 does not corrupt the freshly written FAT; resolve the mount point by filesystem identity (same as JGrub.getMountPoint) rather than by path substring. Enabled by caching the InputContext/OutputContext so values pre-set in start() survive the inherited super.start() run.
1 parent a4625cf commit 564f044

3 files changed

Lines changed: 165 additions & 22 deletions

File tree

distr/src/install/org/jnode/install/action/ActionConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@
2525
*/
2626
public interface ActionConstants {
2727
String INSTALL_ROOT_DIR = "INSTALL_ROOT_DIR";
28+
String DEVICE_ID = "DEVICE_ID";
2829
}

distr/src/install/org/jnode/install/action/GrubInstallerAction.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,17 @@ public ActionInput getInput(final InputContext inContext) {
4545
return new ActionInput() {
4646
public AbstractInstaller.Step collect() {
4747
try {
48-
String deviceID =
49-
inContext.getStringInput("Enter the installation disk device name (example: hda0) : ");
48+
String deviceID = inContext.getStringValue(ActionConstants.DEVICE_ID);
49+
if (deviceID == null || deviceID.trim().length() == 0) {
50+
deviceID =
51+
inContext.getStringInput("Enter the installation disk device name (example: hda0) : ");
52+
if (deviceID == null || deviceID.trim().length() == 0) {
53+
return AbstractInstaller.Step.back;
54+
}
55+
deviceID = deviceID.trim();
56+
inContext.setStringValue(ActionConstants.DEVICE_ID, deviceID);
57+
}
58+
deviceID = deviceID.trim();
5059

5160
Device disk = DeviceUtils.getDevice(deviceID);
5261
JGrub jgrub = new JGrub(new PrintWriter(new OutputStreamWriter(System.out)), disk);

distr/src/install/org/jnode/install/cmdline/CommandLineInstaller.java

Lines changed: 153 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,51 +21,184 @@
2121
package org.jnode.install.cmdline;
2222

2323
import java.io.BufferedReader;
24+
import java.io.File;
2425
import java.io.IOException;
2526
import java.io.InputStreamReader;
27+
import java.util.Map;
28+
29+
import org.jnode.driver.Device;
30+
import org.jnode.driver.DeviceUtils;
31+
import org.jnode.fs.FileSystem;
32+
import org.jnode.fs.FileSystemType;
33+
import org.jnode.fs.service.FileSystemService;
2634
import org.jnode.install.AbstractInstaller;
2735
import org.jnode.install.InputContext;
2836
import org.jnode.install.OutputContext;
37+
import org.jnode.install.action.ActionConstants;
2938
import org.jnode.install.action.CopyFilesAction;
3039
import org.jnode.install.action.GrubInstallerAction;
40+
import org.jnode.naming.InitialNaming;
3141

3242
/**
3343
* @author Levente S\u00e1ntha
3444
*/
3545
public class CommandLineInstaller extends AbstractInstaller {
3646

47+
private final String deviceId;
48+
private InputContext inputContext;
49+
private OutputContext outputContext;
50+
3751
public CommandLineInstaller() {
38-
//grub
39-
actionList.add(new GrubInstallerAction());
40-
//files
52+
this(null);
53+
}
54+
55+
public CommandLineInstaller(String deviceId) {
56+
this.deviceId = (deviceId == null || deviceId.trim().length() == 0) ? null : deviceId.trim();
57+
//files first (before GRUB corrupts the FAT)
4158
actionList.add(new CopyFilesAction());
59+
//grub last
60+
actionList.add(new GrubInstallerAction());
4261
}
4362

4463
public static void main(String... argv) {
45-
new CommandLineInstaller().start();
64+
String deviceId = (argv.length > 0) ? argv[0] : null;
65+
new CommandLineInstaller(deviceId).start();
4666
}
4767

68+
public void start() {
69+
InputContext in = getInputContext();
70+
OutputContext out = getOutputContext();
4871

49-
protected InputContext getInputContext() {
50-
return new InputContext() {
51-
private BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
52-
53-
public String getStringInput(String message) {
54-
try {
55-
System.out.println(message);
56-
return in.readLine();
57-
} catch (IOException e) {
58-
throw new RuntimeException(e);
72+
// Resolve target device: CLI arg, auto-discovered JFAT, then prompt.
73+
String deviceID = this.deviceId;
74+
if (deviceID == null) {
75+
deviceID = in.getStringValue(ActionConstants.DEVICE_ID);
76+
}
77+
if (deviceID == null) {
78+
deviceID = discoverDevice();
79+
}
80+
if (deviceID == null) {
81+
deviceID = in.getStringInput(
82+
"Enter the installation disk device name (example: hda0) : ");
83+
}
84+
if (deviceID == null || deviceID.trim().length() == 0) {
85+
out.showMessage("Error: no device specified");
86+
return;
87+
}
88+
deviceID = deviceID.trim();
89+
90+
// Validate device and resolve its mount point (same identity
91+
// comparison as JGrub.getMountPoint, not a substring match).
92+
try {
93+
Device device = DeviceUtils.getDevice(deviceID);
94+
String mountPoint = getMountPoint(device);
95+
if (mountPoint == null) {
96+
out.showMessage("Error: no mount point found for " + deviceID);
97+
return;
98+
}
99+
if (!mountPoint.endsWith(File.separator)) {
100+
mountPoint += File.separatorChar;
101+
}
102+
in.setStringValue(ActionConstants.DEVICE_ID, deviceID);
103+
in.setStringValue(ActionConstants.INSTALL_ROOT_DIR, mountPoint);
104+
out.showMessage("Installing to " + deviceID + " at " + mountPoint);
105+
} catch (Exception e) {
106+
out.showMessage("Error: " + e.getMessage());
107+
return;
108+
}
109+
110+
super.start();
111+
}
112+
113+
/**
114+
* Find the mount point for the given device by filesystem identity.
115+
* Returns null when the device has no mounted filesystem.
116+
*/
117+
private String getMountPoint(Device device) {
118+
try {
119+
FileSystemService fss = InitialNaming.lookup(FileSystemService.NAME);
120+
FileSystem<?> target = fss.getFileSystem(device);
121+
if (target == null) {
122+
return null;
123+
}
124+
Map<String, FileSystem<?>> mountPoints = fss.getMountPoints();
125+
for (Map.Entry<String, FileSystem<?>> entry : mountPoints.entrySet()) {
126+
if (entry.getValue() == target) {
127+
return entry.getKey();
59128
}
60129
}
61-
};
130+
return null;
131+
} catch (Exception e) {
132+
return null;
133+
}
62134
}
63135

64-
protected OutputContext getOutputContext() {
65-
return new OutputContext() {
66-
public void showMessage(String msg) {
67-
System.out.println(msg);
136+
/**
137+
* Auto-discover a JFAT partition suitable for installation.
138+
* Returns the device name (e.g. "hda1") if exactly one is found, null otherwise.
139+
*/
140+
private String discoverDevice() {
141+
try {
142+
FileSystemService fss = InitialNaming.lookup(FileSystemService.NAME);
143+
Map<String, FileSystem<?>> mountPoints = fss.getMountPoints();
144+
String candidate = null;
145+
for (Map.Entry<String, FileSystem<?>> entry : mountPoints.entrySet()) {
146+
String path = entry.getKey();
147+
FileSystem<?> fs = entry.getValue();
148+
if (path == null || fs == null || !path.startsWith("/devices/")) {
149+
continue;
150+
}
151+
// First segment under /devices/, e.g. "/devices/hda1" -> "hda1".
152+
String rest = path.substring("/devices/".length());
153+
int slash = rest.indexOf('/');
154+
String devName = (slash < 0) ? rest : rest.substring(0, slash);
155+
if (devName.length() == 0 || devName.startsWith("sg")) {
156+
// Skip CDROM drives (sg0, ...) and empty segments.
157+
continue;
158+
}
159+
FileSystemType<?> type = fs.getType();
160+
if (type == null || !"JFAT".equalsIgnoreCase(type.getName())) {
161+
continue;
162+
}
163+
if (candidate == null) {
164+
candidate = devName;
165+
} else if (!candidate.equals(devName)) {
166+
// Multiple candidates - can't auto-discover
167+
return null;
168+
}
68169
}
69-
};
170+
return candidate;
171+
} catch (Exception e) {
172+
return null;
173+
}
174+
}
175+
176+
protected InputContext getInputContext() {
177+
if (inputContext == null) {
178+
inputContext = new InputContext() {
179+
private BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
180+
181+
public String getStringInput(String message) {
182+
try {
183+
System.out.println(message);
184+
return in.readLine();
185+
} catch (IOException e) {
186+
throw new RuntimeException(e);
187+
}
188+
}
189+
};
190+
}
191+
return inputContext;
192+
}
193+
194+
protected OutputContext getOutputContext() {
195+
if (outputContext == null) {
196+
outputContext = new OutputContext() {
197+
public void showMessage(String msg) {
198+
System.out.println(msg);
199+
}
200+
};
201+
}
202+
return outputContext;
70203
}
71204
}

0 commit comments

Comments
 (0)