diff --git a/iris-gui-README.md b/iris-gui-README.md index f867e853..1254cfdb 100644 --- a/iris-gui-README.md +++ b/iris-gui-README.md @@ -156,7 +156,7 @@ IRIX with a media-change Unit Attention, no restart needed. | --- | --- | | **General** | Platform, CPU, cache model (auto/full/shadow), CP0 Count clock, JIT compile threads and persistent code cache (jitv2 builds), RTC offset, graphics board (Newport, GR2 XZ/Extreme, IMPACT Solid/High/Maximum), Newport heads/resolution, PROM, NVRAM, motherboard EEPROM, ttyd1 serial log, monitor and serial ports | | **Disks** | SCSI devices: image paths, CD-ROM discs, COW overlay, scratch volume, DaynaPort, second controller (IP22) | -| **Networking** | Ethernet address (`eaddr`, written to NVRAM on Indy or the NVRAM EEPROM on Indigo2/IP28 while stopped), NAT subnet (applied live, with conflict checks against host interfaces), port forwards (added/removed live), TFTP boot folder (NAT mode), NFS share, PCAP interface, **Check networking** diagnostics | +| **Networking** | Ethernet address (`eaddr`, written to NVRAM on Indy or the NVRAM EEPROM on Indigo2/IP28 while stopped), NAT subnet (applied live, with conflict checks against host interfaces), port forwards (added/removed live), TFTP boot folder and rsh install folder (NAT mode), NFS share, PCAP interface, **Check networking** diagnostics | | **Memory** | RAM banks and the resulting total | | **Display** | Display resolution, window scale, headless, audio on/off and buffering, keyboard shape (ANSI/ISO/JIS) | | **Video-In** | VINO source: **off** (default), **test_pattern**, **camera** (with a Test Camera preview), **black**; standard; camera index | @@ -241,7 +241,7 @@ New disk images default to `disks/scsiN.raw`. External images stay in place; the TOML keeps them as absolute paths (`/Volumes/Media/root.chd`), exactly as selected or typed. A hand-written relative path such as `../../media/root.chd` is also preserved as written. -The same resolution applies to disc changers, NFS shares, TFTP folders, serial +The same resolution applies to disc changers, NFS shares, TFTP and rsh folders, serial logs, ELF binaries, test dumps, filesystem CI sockets, and JIT cache folders. TCP CI addresses and network interface names retain their original spelling. diff --git a/iris-gui/locales/en/config.ftl b/iris-gui/locales/en/config.ftl index 17b8895f..a10ee2f9 100644 --- a/iris-gui/locales/en/config.ftl +++ b/iris-gui/locales/en/config.ftl @@ -247,6 +247,15 @@ config-tftp-boot = To boot a kernel from it, press Esc at “Starting up the sys # $gateway is the server's IP address. config-tftp-irix = From IRIX, the server is { $gateway }: +config-rsh = rsh server +config-rsh-subtitle = +config-rsh-enable = Serve a folder over rsh (install source) +config-rsh-dir = Folder +config-rsh-dir-empty = Choose a folder to turn the rsh server on. +config-rsh-dir-missing = That folder doesn't exist. +config-rsh-inst = To install from it, give inst this location (the folder is the root): +config-rsh-readonly = Read-only: the guest can list and read files, nothing else. + config-nfs = NFS share config-nfs-subtitle = config-nfs-enable = Share a folder over NFS diff --git a/iris-gui/src/config_ui.rs b/iris-gui/src/config_ui.rs index edd57eed..7acd63fd 100644 --- a/iris-gui/src/config_ui.rs +++ b/iris-gui/src/config_ui.rs @@ -1212,6 +1212,8 @@ fn show_network( ui.separator(); out.changed |= tftp_section(ui, cfg, host); + ui.separator(); + out.changed |= rsh_section(ui, cfg, host); } // end: NAT-only section (hidden in PCAP mode) ui.separator(); @@ -1442,6 +1444,56 @@ fn tftp_section(ui: &mut Ui, cfg: &mut MachineConfig, host: &[crate::netplan::Ho changed } +/// NAT mode: the gateway's built-in read-only rsh server (`[network] rsh_dir`), +/// a network installation source for IRIX `inst`. Unset turns the server off +/// (rsh connections then go to the host). Read at Start. Returns true if the +/// setting changed. +fn rsh_section(ui: &mut Ui, cfg: &mut MachineConfig, host: &[crate::netplan::HostIface]) -> bool { + let mut changed = false; + ui.strong(t!("config-rsh")); + subtitle(ui, t!("config-rsh-subtitle")); + let mut on = cfg.network.rsh_dir.is_some(); + if ui.checkbox(&mut on, t!("config-rsh-enable")).changed() { + cfg.network.rsh_dir = if on { + // A bare relative name in this machine's own folder, like + // `tftpboot`; created now so an enabled server never serves an + // empty path. + let dir = "rshroot"; + let _ = std::fs::create_dir_all(dir); + Some(dir.to_string()) + } else { + None + }; + changed = true; + } + if let Some(dir) = cfg.network.rsh_dir.as_mut() { + Grid::new("rsh_grid").num_columns(2).striped(true).show(ui, |ui| { + ui.label(t!("config-rsh-dir")); + changed |= path_row(ui, "rsh_dir", dir, Pick::Dir, &any_filters()).changed; + ui.end_row(); + }); + if dir.trim().is_empty() { + ui.label(RichText::new(t!("config-rsh-dir-empty")).weak().small()); + } else if !Path::new(dir.trim()).is_dir() { + ui.label(RichText::new(t!("config-rsh-dir-missing")) + .color(Color32::from_rgb(0xd9, 0x9a, 0x3d)).small()); + } + let (b, p) = crate::netplan::parse_cidr(cfg.nat_subnet.as_deref()); + let gw = crate::netplan::classify(b, p, host).derived + .map(|d| d.gateway.to_string()) + .unwrap_or_else(|| "192.168.0.1".into()); + ui.label(RichText::new(t!("config-rsh-inst")).weak()); + ui.code(format!("{gw}:/")); + ui.label(RichText::new(t!("config-rsh-readonly")).weak().small()); + } + // A blank field means off, not "serve the working directory". + if cfg.network.rsh_dir.as_deref().is_some_and(|d| d.trim().is_empty()) && !ui.memory(|m| m.focused().is_some()) { + cfg.network.rsh_dir = None; + changed = true; + } + changed +} + /// PCAP mode: edit the virtual NFS host's IPv4 address (`cfg.network.nfs_pcap_ip`). /// The server answers ARP + portmap/NFS/mountd UDP for this single address on the /// bridged LAN, so it only needs an IP on the guest's subnet — no netmask or