A simple, zero-configuration script to quickly boot FreeBSD ISO images using QEMU
at main 138 lines 3.8 kB view raw
1import chalk from "chalk"; 2import { Data, Effect } from "effect"; 3 4export class NetworkError extends Data.TaggedError("NetworkError")<{ 5 cause?: unknown; 6}> {} 7 8export class BridgeSetupError extends Data.TaggedError("BridgeSetupError")<{ 9 cause?: unknown; 10}> {} 11 12export const setupQemuBridge = (bridgeName: string) => 13 Effect.tryPromise({ 14 try: async () => { 15 const bridgeConfPath = "/etc/qemu/bridge.conf"; 16 const bridgeConfContent = await Deno.readTextFile(bridgeConfPath).catch( 17 () => "", 18 ); 19 if (bridgeConfContent.includes(`allow ${bridgeName}`)) { 20 console.log( 21 chalk.greenBright( 22 `QEMU bridge configuration for ${bridgeName} already exists.`, 23 ), 24 ); 25 return; 26 } 27 28 console.log( 29 chalk.blueBright( 30 `Adding QEMU bridge configuration for ${bridgeName}...`, 31 ), 32 ); 33 34 const cmd = new Deno.Command("sudo", { 35 args: [ 36 "sh", 37 "-c", 38 `mkdir -p /etc/qemu && echo "allow ${bridgeName}" >> ${bridgeConfPath}`, 39 ], 40 stdin: "inherit", 41 stdout: "inherit", 42 stderr: "inherit", 43 }); 44 const status = await cmd.spawn().status; 45 46 if (!status.success) { 47 console.error( 48 chalk.redBright( 49 `Failed to add QEMU bridge configuration for ${bridgeName}.`, 50 ), 51 ); 52 Deno.exit(status.code); 53 } 54 55 console.log( 56 chalk.greenBright( 57 `QEMU bridge configuration for ${bridgeName} added successfully.`, 58 ), 59 ); 60 }, 61 catch: (error) => new BridgeSetupError({ cause: error }), 62 }); 63 64export const createBridgeNetworkIfNeeded = ( 65 bridgeName: string, 66) => 67 Effect.tryPromise({ 68 try: async () => { 69 const bridgeExistsCmd = new Deno.Command("ip", { 70 args: ["link", "show", bridgeName], 71 stdout: "null", 72 stderr: "null", 73 }); 74 75 const bridgeExistsStatus = await bridgeExistsCmd.spawn().status; 76 if (bridgeExistsStatus.success) { 77 console.log( 78 chalk.greenBright(`Network bridge ${bridgeName} already exists.`), 79 ); 80 await setupQemuBridge(bridgeName); 81 return; 82 } 83 84 console.log(chalk.blueBright(`Creating network bridge ${bridgeName}...`)); 85 const createBridgeCmd = new Deno.Command("sudo", { 86 args: ["ip", "link", "add", bridgeName, "type", "bridge"], 87 stdin: "inherit", 88 stdout: "inherit", 89 stderr: "inherit", 90 }); 91 92 let status = await createBridgeCmd.spawn().status; 93 if (!status.success) { 94 console.error( 95 chalk.redBright(`Failed to create network bridge ${bridgeName}.`), 96 ); 97 Deno.exit(status.code); 98 } 99 100 const bringUpBridgeCmd = new Deno.Command("sudo", { 101 args: ["ip", "link", "set", "dev", bridgeName, "up"], 102 stdin: "inherit", 103 stdout: "inherit", 104 stderr: "inherit", 105 }); 106 status = await bringUpBridgeCmd.spawn().status; 107 if (!status.success) { 108 console.error( 109 chalk.redBright(`Failed to bring up network bridge ${bridgeName}.`), 110 ); 111 Deno.exit(status.code); 112 } 113 114 console.log( 115 chalk.greenBright(`Network bridge ${bridgeName} created and up.`), 116 ); 117 118 await setupQemuBridge(bridgeName); 119 }, 120 catch: (error) => new NetworkError({ cause: error }), 121 }); 122 123export const generateRandomMacAddress = () => 124 Effect.sync(() => { 125 const hexDigits = "0123456789ABCDEF"; 126 let macAddress = "52:54:00"; 127 128 for (let i = 0; i < 3; i++) { 129 macAddress += ":"; 130 for (let j = 0; j < 2; j++) { 131 macAddress += hexDigits.charAt( 132 Math.floor(Math.random() * hexDigits.length), 133 ); 134 } 135 } 136 137 return macAddress; 138 });