|
21 | 21 | package org.jnode.install.cmdline; |
22 | 22 |
|
23 | 23 | import java.io.BufferedReader; |
| 24 | +import java.io.File; |
24 | 25 | import java.io.IOException; |
25 | 26 | 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; |
26 | 34 | import org.jnode.install.AbstractInstaller; |
27 | 35 | import org.jnode.install.InputContext; |
28 | 36 | import org.jnode.install.OutputContext; |
| 37 | +import org.jnode.install.action.ActionConstants; |
29 | 38 | import org.jnode.install.action.CopyFilesAction; |
30 | 39 | import org.jnode.install.action.GrubInstallerAction; |
| 40 | +import org.jnode.naming.InitialNaming; |
31 | 41 |
|
32 | 42 | /** |
33 | 43 | * @author Levente S\u00e1ntha |
34 | 44 | */ |
35 | 45 | public class CommandLineInstaller extends AbstractInstaller { |
36 | 46 |
|
| 47 | + private final String deviceId; |
| 48 | + private InputContext inputContext; |
| 49 | + private OutputContext outputContext; |
| 50 | + |
37 | 51 | 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) |
41 | 58 | actionList.add(new CopyFilesAction()); |
| 59 | + //grub last |
| 60 | + actionList.add(new GrubInstallerAction()); |
42 | 61 | } |
43 | 62 |
|
44 | 63 | public static void main(String... argv) { |
45 | | - new CommandLineInstaller().start(); |
| 64 | + String deviceId = (argv.length > 0) ? argv[0] : null; |
| 65 | + new CommandLineInstaller(deviceId).start(); |
46 | 66 | } |
47 | 67 |
|
| 68 | + public void start() { |
| 69 | + InputContext in = getInputContext(); |
| 70 | + OutputContext out = getOutputContext(); |
48 | 71 |
|
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(); |
59 | 128 | } |
60 | 129 | } |
61 | | - }; |
| 130 | + return null; |
| 131 | + } catch (Exception e) { |
| 132 | + return null; |
| 133 | + } |
62 | 134 | } |
63 | 135 |
|
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 | + } |
68 | 169 | } |
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; |
70 | 203 | } |
71 | 204 | } |
0 commit comments