├── .github └── workflows │ └── run.yml ├── .gitignore ├── README.md ├── default.nix ├── flake.lock ├── flake.nix ├── mame ├── 001-use-absolute-paths.diff └── default.nix ├── overlay.nix ├── run ├── default.nix ├── go.mod ├── go.sum └── main.go ├── script.nix └── vm.nix /.github/workflows/run.yml: -------------------------------------------------------------------------------- 1 | name: run 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | basic-run: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: DeterminateSystems/nix-installer-action@main 11 | - name: basic 12 | run: | 13 | nix run '.#setup-vm' 14 | response=$(echo 'echo $sysname' | nix run '.#run-vm' -- -nogui) 15 | echo $response 16 | if [ $response != "cirno" ]; then exit 12; fi 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | 9front.*.qcow2 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 9front-in-a-box 2 | 3 | This repo provides a nix flake for automating the setup 4 | and running of 9front virtual machines. Virtual machines 5 | are run using qemu and connecting to them is done with 6 | drawterm. 7 | 8 | These are made as a sort of demo, to reduce the friction 9 | for trying out the system without needing to futz with 10 | qemu and configuration. They are not (really) intended 11 | to be used generally for running 9front servers, but 12 | may serve as a good starting point for those interested 13 | in doing so. 14 | 15 | ## Setup 16 | 17 | The vms are configured in a way where a read only "parent" 18 | qcow2 is kept in the nix store and used as the backing file 19 | for the writable COW image. As such a first time setup needs 20 | to be run to create this new writable qcow2 image first. 21 | 22 | ``` 23 | nix run github:majiru/9front-in-a-box#setup-vm 24 | ``` 25 | 26 | Will download the source qcow2, configure it and place a 27 | `9front.amd64.qcow2` in to the current directory. Because 28 | the parent is read only and kept around, if for some reason 29 | you have imploded your disk you may rerun the setup without 30 | needing to redownload and configure the parent qcow2. 31 | 32 | ## Running 33 | 34 | ``` 35 | nix run github:majiru/9front-in-a-box#run-vm 36 | ``` 37 | 38 | Will then start the new virtual machine and run drawterm 39 | to connect to it. The password for glenda is 'password'. 40 | 41 | Instructions for using rio for first timers may be found in the 42 | [9front FQA](http://fqa.9front.org/fqa8.html). 43 | 44 | ## Tunables 45 | 46 | This flake also provides scripts for using cwfs or gefs instead of hjfs and 47 | also is capable of running the arm64 virtual machines of 9front as well. 48 | 49 | These different configurations are exposed via different packages, the convention 50 | is `setup-vm-$FS-$ARCH` and `run-vm-$FS-$ARCH`. So to run a cwfs arm64 install you 51 | may use `run-vm-cwfs-arm64`. 52 | 53 | 54 | ### Drawterm 55 | 56 | Drawterm is the graphical program used to connect to the virtual machine. 57 | This flake will use a copy of drawterm from nixpkgs that is built for 58 | X11 and pulseaudio. Nixpkgs also contains a drawterm build (`drawterm-wayland`) for wayland 59 | and pipewire. The drawterm binary used by `run-vm` may be changed by passing a `-dt` flag. 60 | 61 | ``` 62 | # Use the wayland drawterm 63 | nix-shell -p drawterm-wayland 64 | # or with nix shell 65 | nix shell 'nixpkgs#drawterm-wayland' 66 | 67 | nix run 'github:majiru/9front-in-a-box#run-vm' -- -dt drawterm 68 | ``` 69 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import { overlays = [ (import ./overlay.nix) ]; }; 3 | in 4 | pkgs.vm9 5 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1737062831, 24 | "narHash": "sha256-Tbk1MZbtV2s5aG+iM99U8FqwxU/YNArMcWAv6clcsBc=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "5df43628fdf08d642be8ba5b3625a6c70731c19c", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "id": "nixpkgs", 32 | "ref": "nixos-unstable", 33 | "type": "indirect" 34 | } 35 | }, 36 | "root": { 37 | "inputs": { 38 | "flake-utils": "flake-utils", 39 | "nixpkgs": "nixpkgs" 40 | } 41 | }, 42 | "systems": { 43 | "locked": { 44 | "lastModified": 1681028828, 45 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 46 | "owner": "nix-systems", 47 | "repo": "default", 48 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 49 | "type": "github" 50 | }, 51 | "original": { 52 | "owner": "nix-systems", 53 | "repo": "default", 54 | "type": "github" 55 | } 56 | } 57 | }, 58 | "root": "root", 59 | "version": 7 60 | } 61 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A flake for running virtual 9front systems"; 3 | 4 | inputs = { 5 | nixpkgs.url = "nixpkgs/nixos-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = 10 | { 11 | self, 12 | nixpkgs, 13 | flake-utils, 14 | }: 15 | flake-utils.lib.simpleFlake { 16 | inherit self nixpkgs; 17 | name = "vm9"; 18 | overlay = ./overlay.nix; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /mame/001-use-absolute-paths.diff: -------------------------------------------------------------------------------- 1 | diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp 2 | index 3defd33d0bb..33daacc4fc8 100644 3 | --- a/src/emu/emuopts.cpp 4 | +++ b/src/emu/emuopts.cpp 5 | @@ -39,16 +39,16 @@ const options_entry emu_options::s_option_entries[] = 6 | { nullptr, nullptr, core_options::option_type::HEADER, "CORE SEARCH PATH OPTIONS" }, 7 | { OPTION_PLUGINDATAPATH, ".", core_options::option_type::PATH, "path to base folder for plugin data (read/write)" }, 8 | { OPTION_MEDIAPATH ";rp;biospath;bp", "roms", core_options::option_type::MULTIPATH, "path to ROM sets and hard disk images" }, 9 | - { OPTION_HASHPATH ";hash_directory;hash", "hash", core_options::option_type::MULTIPATH, "path to software definition files" }, 10 | - { OPTION_SAMPLEPATH ";sp", "samples", core_options::option_type::MULTIPATH, "path to audio sample sets" }, 11 | - { OPTION_ARTPATH, "artwork", core_options::option_type::MULTIPATH, "path to artwork files" }, 12 | - { OPTION_CTRLRPATH, "ctrlr", core_options::option_type::MULTIPATH, "path to controller definitions" }, 13 | - { OPTION_INIPATH, ".;ini;ini/presets", core_options::option_type::MULTIPATH, "path to ini files" }, 14 | - { OPTION_FONTPATH, ".", core_options::option_type::MULTIPATH, "path to font files" }, 15 | + { OPTION_HASHPATH ";hash_directory;hash", "hash;@mamePath@/hash", core_options::option_type::MULTIPATH, "path to software definition files" }, 16 | + { OPTION_SAMPLEPATH ";sp", "samples;@mamePath@/samples", core_options::option_type::MULTIPATH, "path to audio sample sets" }, 17 | + { OPTION_ARTPATH, "artwork;@mamePath@/artwork", core_options::option_type::MULTIPATH, "path to artwork files" }, 18 | + { OPTION_CTRLRPATH, "ctrlr;@mamePath@/ctrlr", core_options::option_type::MULTIPATH, "path to controller definitions" }, 19 | + { OPTION_INIPATH, ".;ini;ini/presets;@mamePath@/ini/presets", core_options::option_type::MULTIPATH, "path to ini files" }, 20 | + { OPTION_FONTPATH, ".;@mamePath@", core_options::option_type::MULTIPATH, "path to font files" }, 21 | { OPTION_CHEATPATH, "cheat", core_options::option_type::MULTIPATH, "path to cheat files" }, 22 | { OPTION_CROSSHAIRPATH, "crosshair", core_options::option_type::MULTIPATH, "path to crosshair files" }, 23 | - { OPTION_PLUGINSPATH, "plugins", core_options::option_type::MULTIPATH, "path to plugin files" }, 24 | - { OPTION_LANGUAGEPATH, "language", core_options::option_type::MULTIPATH, "path to UI translation files" }, 25 | + { OPTION_PLUGINSPATH, "plugins;@mamePath@/plugins", core_options::option_type::MULTIPATH, "path to plugin files" }, 26 | + { OPTION_LANGUAGEPATH, "language;@mamePath@/language", core_options::option_type::MULTIPATH, "path to UI translation files" }, 27 | { OPTION_SWPATH, "software", core_options::option_type::MULTIPATH, "path to loose software" }, 28 | 29 | // output directory options 30 | -------------------------------------------------------------------------------- /mame/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | fetchFromGitHub, 5 | alsa-lib, 6 | SDL2, 7 | SDL2_ttf, 8 | copyDesktopItems, 9 | expat, 10 | flac, 11 | fontconfig, 12 | glm, 13 | installShellFiles, 14 | libXi, 15 | libXinerama, 16 | libjpeg, 17 | libpcap, 18 | libpulseaudio, 19 | makeDesktopItem, 20 | makeWrapper, 21 | papirus-icon-theme, 22 | pkg-config, 23 | portaudio, 24 | portmidi, 25 | pugixml, 26 | python3, 27 | qtbase, 28 | rapidjson, 29 | sqlite, 30 | utf8proc, 31 | which, 32 | writeScript, 33 | zlib, 34 | darwin, 35 | fetchurl, 36 | }: 37 | 38 | let 39 | inherit (darwin.apple_sdk.frameworks) CoreAudioKit ForceFeedback; 40 | indypatch = fetchurl { 41 | url = "https://pastebin.aosc.io/paste/s99mdJMCYWFSvEtOExVAdg/raw"; 42 | hash = "sha256-FgnnJiTcXEBGP4klF6slCI5M0Dl2SVaU0EKMNtswLcg="; 43 | }; 44 | in 45 | stdenv.mkDerivation { 46 | pname = "mame"; 47 | version = "0.254-indy"; 48 | 49 | src = fetchFromGitHub { 50 | owner = "mamedev"; 51 | repo = "mame"; 52 | rev = "6678c80e3e7dec477dcddc83a57904445e69ac6a"; 53 | hash = "sha256-+CLxOy7ik+S51cub9tpOPo7iQ5VI2XyMv6gditXeAWI="; 54 | }; 55 | 56 | makeFlags = [ 57 | "CC=${stdenv.cc.targetPrefix}cc" 58 | "CXX=${stdenv.cc.targetPrefix}c++" 59 | "TOOLS=0" 60 | "USE_LIBSDL=1" 61 | # "USE_SYSTEM_LIB_ASIO=1" 62 | "USE_SYSTEM_LIB_EXPAT=1" 63 | "USE_SYSTEM_LIB_FLAC=1" 64 | "USE_SYSTEM_LIB_GLM=1" 65 | "USE_SYSTEM_LIB_JPEG=1" 66 | # https://www.mamedev.org/?p=523 67 | # "USE_SYSTEM_LIB_LUA=1" 68 | "USE_SYSTEM_LIB_PORTAUDIO=1" 69 | "USE_SYSTEM_LIB_PORTMIDI=1" 70 | "USE_SYSTEM_LIB_PUGIXML=1" 71 | "USE_SYSTEM_LIB_RAPIDJSON=1" 72 | "USE_SYSTEM_LIB_UTF8PROC=1" 73 | "USE_SYSTEM_LIB_SQLITE3=1" 74 | "USE_SYSTEM_LIB_ZLIB=1" 75 | "SOURCES=src/mame/sgi/indy_indigo2.cpp" 76 | "REGENIE=1" 77 | ]; 78 | 79 | dontWrapQtApps = true; 80 | 81 | # https://docs.mamedev.org/initialsetup/compilingmame.html 82 | buildInputs = 83 | [ 84 | expat 85 | zlib 86 | flac 87 | portmidi 88 | portaudio 89 | utf8proc 90 | libjpeg 91 | rapidjson 92 | pugixml 93 | glm 94 | SDL2 95 | SDL2_ttf 96 | sqlite 97 | qtbase 98 | ] 99 | ++ lib.optionals stdenv.isLinux [ 100 | alsa-lib 101 | libpulseaudio 102 | libXinerama 103 | libXi 104 | fontconfig 105 | ] 106 | ++ lib.optionals stdenv.isDarwin [ 107 | libpcap 108 | CoreAudioKit 109 | ForceFeedback 110 | ]; 111 | 112 | nativeBuildInputs = [ 113 | copyDesktopItems 114 | installShellFiles 115 | makeWrapper 116 | pkg-config 117 | python3 118 | which 119 | ]; 120 | 121 | patches = [ 122 | # by default MAME assumes that paths with stock resources are relative and 123 | # that you run MAME changing to install directory, so we add absolute paths 124 | # here 125 | ./001-use-absolute-paths.diff 126 | indypatch 127 | ]; 128 | 129 | # Since the bug described in https://github.com/NixOS/nixpkgs/issues/135438, 130 | # it is not possible to use substituteAll 131 | postPatch = '' 132 | substituteInPlace src/emu/emuopts.cpp \ 133 | --subst-var-by mamePath "$out/opt/mame" 134 | ''; 135 | 136 | desktopItems = [ 137 | (makeDesktopItem { 138 | name = "MAME"; 139 | desktopName = "MAME"; 140 | exec = "mame"; 141 | icon = "mame"; 142 | type = "Application"; 143 | genericName = "MAME is a multi-purpose emulation framework"; 144 | comment = "Play vintage games using the MAME emulator"; 145 | categories = [ 146 | "Game" 147 | "Emulator" 148 | ]; 149 | keywords = [ 150 | "Game" 151 | "Emulator" 152 | "Arcade" 153 | ]; 154 | }) 155 | ]; 156 | 157 | # TODO: copy shaders from src/osd/modules/opengl/shader/glsl*.*h 158 | # to the final package after we figure out how they work 159 | installPhase = 160 | let 161 | icon = "${papirus-icon-theme}/share/icons/Papirus/32x32/apps/mame.svg"; 162 | in 163 | '' 164 | runHook preInstall 165 | 166 | # mame 167 | mkdir -p $out/opt/mame 168 | 169 | install -Dm755 mame -t $out/bin 170 | install -Dm644 ${icon} $out/share/icons/hicolor/scalable/apps/mame.svg 171 | installManPage docs/man/*.1 docs/man/*.6 172 | cp -ar {artwork,bgfx,plugins,language,ctrlr,keymaps,hash} $out/opt/mame 173 | 174 | runHook postInstall 175 | ''; 176 | 177 | enableParallelBuilding = true; 178 | 179 | passthru.updateScript = writeScript "mame-update-script" '' 180 | #!/usr/bin/env nix-shell 181 | #!nix-shell -i bash -p curl common-updater-scripts jq 182 | 183 | set -eu -o pipefail 184 | 185 | latest_version=$(curl -s https://api.github.com/repos/mamedev/mame/releases/latest | jq --raw-output .tag_name) 186 | update-source-version mame "''${latest_version/mame0/0.}" 187 | ''; 188 | 189 | meta = with lib; { 190 | homepage = "https://www.mamedev.org/"; 191 | description = "A multi-purpose emulation framework"; 192 | longDescription = '' 193 | MAME's purpose is to preserve decades of software history. As electronic 194 | technology continues to rush forward, MAME prevents this important 195 | "vintage" software from being lost and forgotten. This is achieved by 196 | documenting the hardware and how it functions. The source code to MAME 197 | serves as this documentation. The fact that the software is usable serves 198 | primarily to validate the accuracy of the documentation (how else can you 199 | prove that you have recreated the hardware faithfully?). Over time, MAME 200 | (originally stood for Multiple Arcade Machine Emulator) absorbed the 201 | sister-project MESS (Multi Emulator Super System), so MAME now documents a 202 | wide variety of (mostly vintage) computers, video game consoles and 203 | calculators, in addition to the arcade video games that were its initial 204 | focus. 205 | ''; 206 | license = with licenses; [ 207 | bsd3 208 | gpl2Plus 209 | ]; 210 | maintainers = with maintainers; [ moody ]; 211 | platforms = platforms.unix; 212 | broken = stdenv.isDarwin; 213 | mainProgram = "mame"; 214 | }; 215 | } 216 | -------------------------------------------------------------------------------- /overlay.nix: -------------------------------------------------------------------------------- 1 | final: prev: 2 | let 3 | callPackage = final.callPackage; 4 | 5 | fsOpts = [ 6 | "hjfs" 7 | "cwfs" 8 | "gefs" 9 | ]; 10 | archOpts = [ 11 | "amd64" 12 | "arm64" 13 | "386" 14 | ]; 15 | 16 | run = callPackage (./run) { }; 17 | 18 | mkvm = 19 | { 20 | fs ? "hjfs", 21 | arch ? "amd64", 22 | }: 23 | callPackage (./vm.nix) { inherit fs arch; }; 24 | 25 | allvm = 26 | [ 27 | { 28 | name = "vm"; 29 | value = mkvm { }; 30 | arch = "amd64"; 31 | } 32 | ] 33 | ++ (map (a: { 34 | name = "vm-${a}"; 35 | value = mkvm { fs = a; }; 36 | arch = "amd64"; 37 | }) fsOpts) 38 | ++ (map (a: { 39 | name = "vm-${a}"; 40 | value = mkvm { arch = a; }; 41 | arch = a; 42 | }) archOpts) 43 | ++ (map 44 | (a: { 45 | name = "vm-${a.fs}-${a.arch}"; 46 | value = mkvm { 47 | inherit (a) fs; 48 | inherit (a) arch; 49 | }; 50 | inherit (a) arch; 51 | }) 52 | ( 53 | prev.lib.attrsets.cartesianProduct { 54 | fs = fsOpts; 55 | arch = archOpts; 56 | } 57 | ) 58 | ); 59 | 60 | mksetup = 61 | { vm, arch }: 62 | callPackage (./script.nix) { 63 | create = "yes"; 64 | inherit run vm arch; 65 | }; 66 | 67 | allsetup = map (a: { 68 | name = "setup-${a.name}"; 69 | value = mksetup { 70 | vm = a.value; 71 | inherit (a) arch; 72 | }; 73 | }) allvm; 74 | 75 | mkrun = 76 | { vm, arch }: 77 | callPackage (./script.nix) { 78 | inherit run; 79 | inherit vm arch; 80 | }; 81 | 82 | allrun = map (a: { 83 | name = "run-${a.name}"; 84 | value = mkrun { 85 | vm = a.value; 86 | inherit (a) arch; 87 | }; 88 | }) allvm; 89 | 90 | mame = { 91 | mame = prev.libsForQt5.callPackage (./mame) { }; 92 | }; 93 | 94 | pkgs = (builtins.listToAttrs (allvm ++ allsetup ++ allrun)) // mame; 95 | in 96 | { 97 | vm9 = pkgs; 98 | } 99 | -------------------------------------------------------------------------------- /run/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | qemu, 3 | makeWrapper, 4 | lib, 5 | buildGoModule, 6 | }: 7 | buildGoModule { 8 | pname = "run"; 9 | version = "0.1"; 10 | 11 | src = ./.; 12 | 13 | vendorHash = "sha256-f1qukX/vDd+dehv9y9pv0NqNt6D/LWZ3ufeJOsqvG2Y="; 14 | 15 | nativeBuildInputs = [ makeWrapper ]; 16 | postFixup = '' 17 | wrapProgram $out/bin/run --prefix PATH : ${lib.makeBinPath [ qemu ]} 18 | ''; 19 | 20 | meta = with lib; { 21 | description = "Manages the 9front-in-a-box vm"; 22 | homepage = "https://github/majiru/9front-in-a-box/run"; 23 | license = licenses.mit; 24 | maintainers = with maintainers; [ moody ]; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /run/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/majiru/9front-in-a-box/run 2 | 3 | go 1.21.4 4 | 5 | require github.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f 6 | 7 | require ( 8 | github.com/golang/protobuf v1.3.3 // indirect 9 | github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect 10 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect 11 | golang.org/x/sys v0.0.0-20190412213103-97732733099d // indirect 12 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect 13 | google.golang.org/grpc v1.31.0 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /run/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 6 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 7 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 8 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 9 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 10 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 11 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 12 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 13 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 14 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 15 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 16 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 17 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 18 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 19 | github.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f h1:7MmqygqdeJtziBUpm4Z9ThROFZUaVGaePMfcDnluf1E= 20 | github.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f/go.mod h1:n1ej5+FqyEytMt/mugVDZLIiqTMO+vsrgY+kM6ohzN0= 21 | github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4= 22 | github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= 23 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 24 | github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4= 25 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 26 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= 27 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 28 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 29 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 30 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 31 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 32 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 33 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 34 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 35 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 36 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= 37 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 38 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 39 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 40 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 41 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 42 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 43 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 45 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 46 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 47 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 48 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 49 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 50 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 51 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 52 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 53 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 54 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 55 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 56 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 57 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 58 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 59 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 60 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 61 | google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= 62 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 63 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 64 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 65 | -------------------------------------------------------------------------------- /run/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/exec" 9 | "os/signal" 10 | "path/filepath" 11 | "regexp" 12 | "strings" 13 | "time" 14 | 15 | expect "github.com/google/goexpect" 16 | ) 17 | 18 | var ( 19 | ramFlag = flag.String("m", "4G", "memory for qemu virtual machine") 20 | cpuFlag = flag.String("cpu", "4", "number of cored for virtual machines") 21 | createFlag = flag.String("create", "", "create a new 9front.qcow2 in the cwd ") 22 | debugFlag = flag.Bool("debug", false, "enable debug output") 23 | archFlag = flag.String("arch", "amd64", "architechture of vm") 24 | ubootFlag = flag.String("uboot", "u-boot.bin", "uboot binary for arm64") 25 | qpathFlag = flag.String("qpath", "", "location of qemu binaries") 26 | drawtermFlag = flag.String("dt", "drawterm", "drawterm binary") 27 | nogui = flag.Bool("nogui", false, "disable the GUI") 28 | ) 29 | 30 | func qemuCmd() []string { 31 | m := map[string][]string{ 32 | "amd64": { 33 | filepath.Join(*qpathFlag, "qemu-system-x86_64"), 34 | "-nic", 35 | "user,hostfwd=tcp::17019-:17019", 36 | "-enable-kvm", 37 | "-m", 38 | *ramFlag, 39 | "-smp", 40 | *cpuFlag, 41 | "-drive", 42 | "file=9front.amd64.qcow2,media=disk,if=virtio,index=0", 43 | "-nographic", 44 | }, 45 | "arm64": { 46 | filepath.Join(*qpathFlag, "qemu-system-aarch64"), 47 | "-M", 48 | "virt-2.12,gic-version=3", 49 | "-cpu", 50 | "cortex-a72", 51 | "-m", 52 | *ramFlag, 53 | "-smp", 54 | *cpuFlag, 55 | "-bios", 56 | *ubootFlag, 57 | "-drive", 58 | "file=9front.arm64.qcow2,if=none,id=disk", 59 | "-device", 60 | "virtio-blk-pci-non-transitional,drive=disk", 61 | "-nic", 62 | "user,hostfwd=tcp::17019-:17019,model=virtio-net-pci-non-transitional", 63 | "-nographic", 64 | }, 65 | "386": { 66 | filepath.Join(*qpathFlag, "qemu-system-x86_64"), 67 | "-nic", 68 | "user,hostfwd=tcp::17019-:17019", 69 | "-enable-kvm", 70 | "-m", 71 | *ramFlag, 72 | "-smp", 73 | *cpuFlag, 74 | "-drive", 75 | "file=9front.386.qcow2,media=disk,if=virtio,index=0", 76 | "-nographic", 77 | }, 78 | } 79 | r, ok := m[*archFlag] 80 | if !ok { 81 | log.Fatal("unsupported arch") 82 | } 83 | return r 84 | } 85 | 86 | func main() { 87 | flag.Parse() 88 | 89 | qcow := "9front." + *archFlag + ".qcow2" 90 | if *createFlag != "" { 91 | err := exec.Command("qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-o", "backing_file="+*createFlag, qcow).Run() 92 | if err != nil { 93 | log.Fatal(err) 94 | } 95 | os.Exit(0) 96 | } 97 | if _, err := os.Stat(qcow); err != nil { 98 | fmt.Fprintf(os.Stderr, "could not find %s\n", qcow) 99 | os.Exit(1) 100 | } 101 | 102 | cm := strings.Join(qemuCmd(), " ") 103 | if *debugFlag { 104 | fmt.Println(cm) 105 | } 106 | exp, _, err := expect.Spawn(cm, -1) 107 | if err != nil { 108 | log.Fatal(err) 109 | } 110 | defer exp.Close() 111 | 112 | if *debugFlag { 113 | exp.Options(expect.Tee(os.Stdout)) 114 | } 115 | exp.Expect(regexp.MustCompile("bootargs is"), -1) 116 | exp.Send("\n") 117 | exp.Expect(regexp.MustCompile("user"), -1) 118 | exp.Send("\n") 119 | exp.Expect(regexp.MustCompile("%"), -1) 120 | exp.Send(`echo 'key proto=dp9ik dom=9front user=glenda !password=password' >/mnt/factotum/ctl` + "\n") 121 | exp.Expect(regexp.MustCompile("%"), -1) 122 | exp.Send("ip/ipconfig -6 ether /net/ether0\n") 123 | exp.Expect(regexp.MustCompile("%"), -1) 124 | exp.Send("ip/ipconfig ether /net/ether0\n") 125 | exp.Expect(regexp.MustCompile("%"), -1) 126 | exp.Send("ip/ipconfig ether /net/ether0 ra6 recvra 1\n") 127 | exp.Expect(regexp.MustCompile("%"), -1) 128 | exp.Send("aux/listen1 -t 'tcp!*!17019' /rc/bin/service/tcp17019 &\n") 129 | exp.Expect(regexp.MustCompile("listen started"), -1) 130 | 131 | exitch := make(chan struct{}) 132 | go func() { 133 | c := make(chan os.Signal, 1) 134 | signal.Notify(c, os.Interrupt) 135 | <-c 136 | exitch <- struct{}{} 137 | }() 138 | go func() { 139 | time.Sleep(2 * time.Second) 140 | if *nogui { 141 | cmd := exec.Command(*drawtermFlag, "-G", "-r", ".", "-u", "glenda", "-h", "127.0.0.1", "-a", "127.0.0.1", "-c", "service=cpu rc -lI") 142 | cmd.Env = append(cmd.Env, "PASS=password") 143 | cmd.Stdin = os.Stdin 144 | cmd.Stdout = os.Stdout 145 | cmd.Stderr = os.Stderr 146 | err := cmd.Run() 147 | if err != nil { 148 | log.Println(err) 149 | } 150 | } else { 151 | exec.Command(*drawtermFlag, "-u", "glenda", "-h", "127.0.0.1", "-a", "127.0.0.1", "-c", "rc", "-c", "console=() service=terminal rc -l").Run() 152 | } 153 | exitch <- struct{}{} 154 | }() 155 | <-exitch 156 | exp.Send("fshalt\n") 157 | exp.Expect(regexp.MustCompile("done halting"), -1) 158 | exp.Close() 159 | } 160 | -------------------------------------------------------------------------------- /script.nix: -------------------------------------------------------------------------------- 1 | { 2 | writeScriptBin, 3 | rc, 4 | run, 5 | vm, 6 | pkgsCross, 7 | qemu, 8 | drawterm, 9 | 10 | arch ? "amd64", 11 | create ? "no", 12 | }: 13 | let 14 | uboot = pkgsCross.aarch64-multiplatform.ubootQemuAarch64; 15 | in 16 | writeScriptBin "run.sh" '' 17 | #!${rc}/bin/rc 18 | 19 | create=${create} 20 | if(~ $create 'yes'){ 21 | ${run}/bin/run -arch ${arch} -create ${vm}/9front.qcow2 22 | exit 23 | } 24 | ${run}/bin/run -qpath ${qemu}/bin -uboot ${uboot}/u-boot.bin -arch ${arch} -dt ${drawterm}/bin/drawterm $* 25 | '' 26 | -------------------------------------------------------------------------------- /vm.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | fetchurl, 5 | qemu, 6 | expect, 7 | writeScript, 8 | pkgsCross, 9 | 10 | fs ? "hjfs", 11 | size ? "50G", 12 | arch ? "amd64", 13 | release ? "11091", 14 | sourceType ? 15 | { 16 | amd64 = "qcow2"; 17 | arm64 = "qcow2"; 18 | "386" = "iso"; 19 | } 20 | .${arch}, 21 | sourceUrl ? "https://iso.only9fans.com/release", 22 | source ? fetchurl { 23 | url = "${sourceUrl}/9front-${release}.${arch}.${sourceType}.gz"; 24 | hash = 25 | { 26 | amd64 = "sha256-bfgHrjDwBLiqfR7IA7ptIys48OqyS3f2NcxAjpwBSRE="; 27 | arm64 = "sha256-hSxS9WkKDZhCPyFNhilhTmKbzUa2gPIy468zOM176Iw="; 28 | "386" = "sha256-5jkhEyBS/10sC7UH0iIfAkvHC7w1EpmEG82MO3IvWM8="; 29 | } 30 | ."${arch}"; 31 | }, 32 | }: 33 | let 34 | uboot = pkgsCross.aarch64-multiplatform.ubootQemuAarch64; 35 | qbin = 36 | { 37 | amd64 = "qemu-system-x86_64 -enable-kvm -m 2G -smp 4 -drive file=./9front.qcow2,media=disk,if=virtio,index=0 -drive file=$env(TARGET),index=1,media=disk,if=virtio -nographic -nic none"; 38 | arm64 = "qemu-system-aarch64 -M virt-2.12,gic-version=3 -cpu cortex-a72 -m 4G -smp 4 -bios ${uboot}/u-boot.bin -drive file=./9front.qcow2,if=none,id=disk1 -drive file=$env(TARGET),index=1,media=disk,if=none,id=disk2 -device virtio-blk-pci-non-transitional,drive=disk1 -device virtio-blk-pci-non-transitional,drive=disk2 -nographic"; 39 | "386" = 40 | "qemu-system-x86_64 -enable-kvm -m 2G -smp 4 -drive file=./9front.iso,media=cdrom -drive file=$env(TARGET),index=1,media=disk,if=virtio -nographic -nic none"; 41 | } 42 | ."${arch}"; 43 | qbin2 = 44 | { 45 | amd64 = "qemu-system-x86_64 -enable-kvm -m 2G -smp 4 -drive file=$env(TARGET),index=1,media=disk,if=virtio -nographic -nic none"; 46 | arm64 = "qemu-system-aarch64 -M virt-2.12,gic-version=3 -cpu cortex-a72 -m 4G -smp 4 -bios ${uboot}/u-boot.bin -drive file=$env(TARGET),index=0,media=disk,if=none,id=disk2 -device virtio-blk-pci-non-transitional,drive=disk2 -nographic"; 47 | "386" = 48 | "qemu-system-x86_64 -enable-kvm -m 2G -smp 4 -drive file=$env(TARGET),index=1,media=disk,if=virtio -nographic -nic none"; 49 | } 50 | ."${arch}"; 51 | 52 | disks.inst = 53 | { 54 | arm64 = "sdG0"; 55 | amd64 = "sdG0"; 56 | "386" = "sdF0"; 57 | } 58 | ."${arch}"; 59 | disks.final = 60 | { 61 | arm64 = "sdF0"; 62 | amd64 = "sdF0"; 63 | "386" = "sdF0"; 64 | } 65 | ."${arch}"; 66 | preboot = 67 | { 68 | amd64 = '' 69 | expect "bootfile=" 70 | send "\n" 71 | expect ">" 72 | send "console=0\n" 73 | expect ">" 74 | send "boot\n" 75 | ''; 76 | arm64 = ""; 77 | "386" = '' 78 | expect "bootfile=" 79 | send "\n" 80 | expect ">" 81 | send "console=0\n" 82 | expect ">" 83 | send "vgasize=text\n" 84 | expect ">" 85 | send "boot\n" 86 | ''; 87 | } 88 | ."${arch}"; 89 | postinst = 90 | { 91 | amd64 = ""; 92 | "386" = ""; 93 | arm64 = '' 94 | send "!rc\n" 95 | expect "%" 96 | send "mount -c /srv/dos /n/9fat /dev/sdG0/9fat\n" 97 | expect "%" 98 | send "mount -c /srv/dos /n/old9fat /dev/sdF0/dos\n" 99 | expect "%" 100 | send "cp /n/old9fat/9qemu.u /n/9fat/\n" 101 | expect "%" 102 | send "cp /n/old9fat/boot.scr /n/9fat/\n" 103 | expect "%" 104 | send "unmount /n/9fat\n" 105 | expect "%" 106 | send "unmount /n/old9fat\n" 107 | expect "%" 108 | send "exit\n" 109 | expect "Task to do" 110 | ''; 111 | } 112 | ."${arch}"; 113 | partSelect = 114 | { 115 | cwfs = '' 116 | expect "Cwfs cache partition" 117 | send "/dev/${disks.inst}/fscache\n" 118 | expect "Cwfs worm partition" 119 | send "/dev/${disks.inst}/fsworm\n" 120 | expect "Cwfs other partition" 121 | send "/dev/${disks.inst}/other\n" 122 | ''; 123 | hjfs = '' 124 | expect "Hjfs partition" 125 | send "/dev/${disks.inst}/fs\n" 126 | expect "Size of RAM filesystem" 127 | send "\n" 128 | ''; 129 | gefs = '' 130 | expect "Gefs partition" 131 | send "/dev/${disks.inst}/fs\n" 132 | ''; 133 | } 134 | ."${fs}"; 135 | expectScript = writeScript "expect.sh" '' 136 | #!${expect}/bin/expect -f 137 | set timeout -1 138 | set debug 5 139 | spawn ${qbin} 140 | ${preboot} 141 | expect "bootargs is" 142 | send "\n" 143 | expect "user" 144 | send "\n" 145 | expect "%" 146 | 147 | send "inst/start\n" 148 | expect "Task to do" 149 | 150 | send "configfs\n" 151 | expect "File system" 152 | send "${if fs == "cwfs" then "cwfs64x" else fs}\n" 153 | expect "Task to do" 154 | 155 | send "partdisk\n" 156 | expect "Disk to partition" 157 | send "${disks.inst}\n" 158 | expect "Install mbr" 159 | send "mbr\n" 160 | expect ">>>" 161 | send "w\n" 162 | expect ">>>" 163 | send "q\n" 164 | expect "Task to do" 165 | 166 | send "prepdisk\n" 167 | expect "Plan 9 partition" 168 | send "/dev/${disks.inst}/plan9\n" 169 | expect ">>>" 170 | send "w\n" 171 | expect ">>>" 172 | send "q\n" 173 | expect "Task to do" 174 | 175 | send "mountfs\n" 176 | ${partSelect} 177 | expect "Ream the filesystem" 178 | send "yes\n" 179 | expect "Task to do" 180 | 181 | send "confignet\n" 182 | expect "Task to do" 183 | 184 | send "mountdist\n" 185 | expect "Distribution disk" 186 | send "/\n" 187 | expect "Location of archives" 188 | send "/\n" 189 | expect "Task to do" 190 | 191 | send "copydist\n" 192 | expect "Task to do" 193 | 194 | send "ndbsetup\n" 195 | expect "sysname" 196 | send "\n" 197 | expect "Task to do" 198 | 199 | send "tzsetup\n" 200 | expect "Time Zone" 201 | expect "W-SU" 202 | send "US_Central\n" 203 | expect "Task to do" 204 | 205 | send "bootsetup\n" 206 | expect "Plan 9 FAT partition" 207 | send "/dev/${disks.inst}/9fat\n" 208 | expect "Install the Plan 9 master" 209 | send "yes\n" 210 | expect "Mark the Plan 9" 211 | send "yes\n" 212 | expect "Task to do" 213 | 214 | ${postinst} 215 | 216 | ${ 217 | if disks.final != disks.inst then 218 | '' 219 | send "!rc\n" 220 | expect "%" 221 | send "mount -c /srv/dos /n/9 /dev/${disks.inst}/9fat\n" 222 | expect "%" 223 | send "sed 's/${disks.inst}/${disks.final}/g' /n/9/plan9.ini >/tmp/plan9.ini\n" 224 | expect "%" 225 | send "mv /tmp/plan9.ini /n/9/plan9.ini\n" 226 | expect "%" 227 | send "unmount /n/9\n" 228 | expect "%" 229 | send "exit\n" 230 | expect "Task to do" 231 | '' 232 | else 233 | "" 234 | } 235 | 236 | send "!rc\n" 237 | expect "%" 238 | send "sed '/#m/d' /n/newfs/usr/glenda/lib/profile > /tmp/profile\n" 239 | expect "%" 240 | send "mv /tmp/profile /n/newfs/usr/glenda/lib/profile\n" 241 | expect "%" 242 | send "exit\n" 243 | expect "Task to do" 244 | 245 | send "finish\n" 246 | expect "done halting" 247 | ''; 248 | fixCwfsConfig = writeScript "expect.sh" '' 249 | #!${expect}/bin/expect -f 250 | set timeout -1 251 | set debug 5 252 | spawn ${qbin2} 253 | ${preboot} 254 | expect "bootargs is" 255 | send "!rc\n" 256 | expect "%" 257 | send "cwfs64x -C -c -f /dev/${disks.final}/fscache\n" 258 | expect "config:" 259 | send "filsys main c(/dev/${disks.final}/fscache)(/dev/${disks.final}/fsworm)\n" 260 | expect "config:" 261 | send "filsys other (/dev/${disks.final}/other)\n" 262 | expect "config:" 263 | send "filsys dump o\n" 264 | expect "config:" 265 | send "end\n" 266 | expect "%" 267 | send "exit\n" 268 | expect "bootargs is" 269 | send "local!/dev/${disks.final}/fscache\n" 270 | expect "user" 271 | send "\n" 272 | expect "%" 273 | send "fshalt\n" 274 | expect "done halting" 275 | ''; 276 | fixHjfsConfig = writeScript "expect.sh" '' 277 | #!${expect}/bin/expect -f 278 | set timeout -1 279 | set debug 5 280 | spawn ${qbin2} 281 | ${preboot} 282 | expect "bootargs is" 283 | send "\n" 284 | expect "user" 285 | send "\n" 286 | expect "%" 287 | send "9fs 9fat\n" 288 | expect "%" 289 | send "echo 'console=0' >> /n/9/plan9.ini\n" 290 | expect "%" 291 | send "sed '/#m/d' /usr/glenda/lib/profile > /tmp/profile\n" 292 | expect "%" 293 | send "mv /tmp/profile /usr/glenda/lib/profile\n" 294 | expect "%" 295 | send "fshalt\n" 296 | expect "done halting" 297 | ''; 298 | in 299 | stdenv.mkDerivation rec { 300 | pname = "vm-${fs}-${arch}"; 301 | version = "0.1"; 302 | 303 | nativeBuildInputs = [ 304 | qemu 305 | expect 306 | ]; 307 | 308 | src = source; 309 | 310 | unpackPhase = '' 311 | gunzip -c ${src} > 9front.${sourceType} 312 | ''; 313 | 314 | buildPhase = 315 | { 316 | cwfs = '' 317 | mkdir -p $out 318 | qemu-img create -f qcow2 tmp.qcow2 ${size} 319 | TARGET="tmp.qcow2" ${expectScript} 320 | TARGET="tmp.qcow2" ${fixCwfsConfig} 321 | mv tmp.qcow2 $out/9front.qcow2 322 | ''; 323 | hjfs = 324 | if arch != "386" then 325 | '' 326 | mkdir -p $out 327 | TARGET="9front.qcow2" ${fixHjfsConfig} 328 | mv 9front.qcow2 $out/ 329 | '' 330 | else 331 | '' 332 | mkdir -p $out 333 | qemu-img create -f qcow2 tmp.qcow2 ${size} 334 | TARGET="tmp.qcow2" ${expectScript} 335 | mv tmp.qcow2 $out/9front.qcow2 336 | ''; 337 | gefs = '' 338 | mkdir -p $out 339 | qemu-img create -f qcow2 tmp.qcow2 ${size} 340 | TARGET="tmp.qcow2" ${expectScript} 341 | mv tmp.qcow2 $out/9front.qcow2 342 | ''; 343 | } 344 | ."${fs}"; 345 | 346 | meta = with lib; { 347 | description = "9front-in-a-box vm"; 348 | homepage = "https://github/majiru/9front-in-a-box/"; 349 | license = licenses.mit; 350 | maintainers = with maintainers; [ moody ]; 351 | }; 352 | 353 | } 354 | --------------------------------------------------------------------------------