From 9cbabb58c916ae488b56d1010b4a9f224d6f3831 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Mon, 29 Sep 2025 23:51:15 -0400 Subject: [PATCH] Switch to chroot-based usage (run from installer ISO) --- README.md | 6 +--- applications.go | 9 +---- configurations.go | 18 +++++----- cosmic.go | 5 ++- go.mod | 5 ++- go.sum | 2 ++ main.go | 38 ++++++++++++++++---- premount.go | 24 +++++++++++++ removals.go | 2 +- system.go | 89 +++++----------------------------------------- universalTweaks.go | 14 +++----- utilities.go | 55 ++++++++++++++++++++-------- 12 files changed, 128 insertions(+), 139 deletions(-) create mode 100644 premount.go diff --git a/README.md b/README.md index acd02c9..5abd33a 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,4 @@ If you intend on using all options provided by this utility, it is best to use s - whichever one you don't need - Firefox and language package -As soon as the system is installed and booted, run this utility as root and follow the prompts! That's it. - -If using to configure Cosmic DE, I recommend running this utility from a raw TTY without Cosmic running. - -A reboot is recommended if applying system tweaks. +As soon as the system is installed (prior to booting into it), run this utility as root and follow the prompts! That's it. diff --git a/applications.go b/applications.go index a980f9e..71d51e6 100644 --- a/applications.go +++ b/applications.go @@ -1,10 +1,7 @@ package main import ( - "os/exec" - "github.com/rwinkhart/go-boilerplate/front" - "github.com/rwinkhart/go-boilerplate/other" ) func applications() { @@ -46,11 +43,7 @@ func applications() { } fallthrough case 7: - cmd := exec.Command("pacman", "-S", "virt-manager", "qemu-desktop", "libvirt", "--noconfirm") - err := cmd.Run() - if err != nil { - other.PrintError("Failed to perform application installations for virt-manager", 1) - } + chrootCommandRun([]string{"pacman", "-S", "virt-manager", "qemu-desktop", "libvirt", "--noconfirm"}) manageService("enable", "libvirtd.service") manageService("start", "libvirtd.service") // TODO manage user groups! diff --git a/configurations.go b/configurations.go index b408e20..327ea11 100644 --- a/configurations.go +++ b/configurations.go @@ -19,39 +19,39 @@ func configurations() { fallthrough case 3: managePackage("-S", "wayland-protocols") - writeFile("/home/"+getUsername()+"/.profile", shellProfile, getUsername(), 0644) + writeFile(mountPoint+"/home/"+getUsername()+"/.profile", shellProfile, getUsername(), 0644) if !doAll { break } fallthrough case 4: - writeFile("/home/"+getUsername()+"/.zshrc", zshrc, getUsername(), 0644) + writeFile(mountPoint+"/home/"+getUsername()+"/.zshrc", zshrc, getUsername(), 0644) if !doAll { break } fallthrough case 5: - writeFile("/etc/xdg/nvim/sysinit.vim", sysinitVim, "root", 0644) + writeFile(mountPoint+"/etc/xdg/nvim/sysinit.vim", sysinitVim, "root", 0644) if !doAll { break } fallthrough case 6: - err := os.MkdirAll("/home/"+getUsername()+"/.config/zed", 0755) + err := os.MkdirAll(mountPoint+"/home/"+getUsername()+"/.config/zed", 0755) if err != nil { other.PrintError("Failed to create zed config directory", 1) } - writeFile("/home/"+getUsername()+"/.config/zed/settings.json", zed, getUsername(), 0600) + writeFile(mountPoint+"/home/"+getUsername()+"/.config/zed/settings.json", zed, getUsername(), 0600) if !doAll { break } fallthrough case 7: - writeFile("/etc/makepkg.conf", makepkg, "root", 0644) + writeFile(mountPoint+"/etc/makepkg.conf", makepkg, "root", 0644) if !doAll { break @@ -59,9 +59,9 @@ func configurations() { fallthrough case 8: managePackage("-S", "pacman-contrib") - writeFile("/etc/pacman.conf", pacman, "root", 0644) - writeFile("/etc/pacman.d/hooks/paccache-clean.hook", pacmanHookClean, "root", 0644) - writeFile("/etc/pacman.d/hooks/nvidia.hook", pacmanHookNvidia, "root", 0644) + writeFile(mountPoint+"/etc/pacman.conf", pacman, "root", 0644) + writeFile(mountPoint+"/etc/pacman.d/hooks/paccache-clean.hook", pacmanHookClean, "root", 0644) + writeFile(mountPoint+"/etc/pacman.d/hooks/nvidia.hook", pacmanHookNvidia, "root", 0644) } } } diff --git a/cosmic.go b/cosmic.go index 2212f65..d8927a8 100644 --- a/cosmic.go +++ b/cosmic.go @@ -22,12 +22,11 @@ func cosmic() { "Tk" + version + "show_maximize": "false"} for key, value := range filenamesToValues { - filePath := "/home/" + getUsername() + "/.config/cosmic/com.system76.Cosmic" + key - writeFile(filePath, value+"\n", getUsername(), 0755) + writeFile(mountPoint+"/home/"+getUsername()+"/.config/cosmic/com.system76.Cosmic"+key, value+"\n", getUsername(), 0755) } // Replace SDDM with cosmic-greeter - writeFile("/etc/greetd/config.toml", "[terminal]\nvt = 1\n\n[default_session]\ncommand = \"cosmic-comp /bin/cosmic-greeter\"\nuser = \"cosmic-greeter\"\n", "root", 0644) + writeFile(mountPoint+"/etc/greetd/config.toml", "[terminal]\nvt = 1\n\n[default_session]\ncommand = \"cosmic-comp /bin/cosmic-greeter\"\nuser = \"cosmic-greeter\"\n", "root", 0644) manageService("stop", "sddm.service") manageService("disable", "sddm.service") manageService("enable", "greetd.service") diff --git a/go.mod b/go.mod index cee071b..5c56945 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,10 @@ module cachy-post go 1.25.1 -require github.com/rwinkhart/go-boilerplate v0.1.0 +require ( + github.com/moby/sys/mountinfo v0.7.2 + github.com/rwinkhart/go-boilerplate v0.1.0 +) require ( golang.org/x/sys v0.33.0 // indirect diff --git a/go.sum b/go.sum index eeff877..0027975 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/rwinkhart/go-boilerplate v0.1.0 h1:EzlVj6R7Bxtl79Nl7R5zRcg6s+Cf2FAqGIzR4giWTQg= github.com/rwinkhart/go-boilerplate v0.1.0/go.mod h1:cnzIF45I0FCOvE4YIB+26pLCUx2kWyY2llKYZruNaRY= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= diff --git a/main.go b/main.go index 61457b4..d1dce0a 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,18 @@ package main import ( + "fmt" + "os" "os/user" "github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/go-boilerplate/other" ) +const mountPoint = "/mnt/cph" + var username string +var partitionR, partitionB string // TODO add mode to perform EVERYTHING func main() { @@ -19,22 +24,41 @@ func main() { other.PrintError("This utility must be run as root", 1) } + partitionR = front.Input("Target root partition (/dev/driveparition):") + partitionB = front.Input("Target boot (efi) partition (/dev/driveparition):") + err = os.MkdirAll("/mnt/cph", 0777) + if err != nil { + other.PrintError("Failed to create mount directory (/mnt/cph)", 1) + } + for { - choice := front.InputMenuGen("Option:", []string{"universal tweaks", "system tweaks", "removals", "applications", "configuration", "cosmic"}) + fmt.Println() + choice := front.InputMenuGen("Option:", []string{"pre-mount tweaks", "universal tweaks", "system tweaks", "removals", "applications", "configuration", "cosmic"}) switch choice { case 1: - universalTweaks() + if partitionIsMounted() { + fmt.Println(partitionR + " is already mounted on " + mountPoint) + } else { + premount() + } case 2: - system() + mountPartition() + universalTweaks() case 3: + mountPartition() + system() + case 4: + mountPartition() removals() // TODO remove config/data - case 4: - applications() - // TODO remove config/data for replacements case 5: - configurations() + mountPartition() + applications() case 6: + mountPartition() + configurations() + case 7: + mountPartition() cosmic() // TODO keyboard shortcuts for screenshot/sticky window/terminal // TODO window decoration position (not yet implemented?) diff --git a/premount.go b/premount.go new file mode 100644 index 0000000..4b5b705 --- /dev/null +++ b/premount.go @@ -0,0 +1,24 @@ +package main + +import ( + "os/exec" + + "github.com/rwinkhart/go-boilerplate/front" + "github.com/rwinkhart/go-boilerplate/other" +) + +func premount() { + for { + choice := front.InputMenuGen("Tweak:", []string{"BACK", "enable ext4 fast_commit"}) + switch choice { + case 1: + return + case 2: + cmd := exec.Command("tune2fs", "-O", "fast_commit", partitionR) + err := cmd.Run() + if err != nil { + other.PrintError("Failed to enable fast_commit", 1) + } + } + } +} diff --git a/removals.go b/removals.go index 92a9e23..149a8f6 100644 --- a/removals.go +++ b/removals.go @@ -8,7 +8,7 @@ import ( func removals() { for { var doAll bool - choice := front.InputMenuGen("Application to remove:", []string{"BACK", "ALL", "linux-cachyos-lts"}) + choice := front.InputMenuGen("Application to remove:", []string{"BACK", "linux-cachyos-lts"}) var target string switch choice { case 1: diff --git a/system.go b/system.go index 000bbbb..acad51b 100644 --- a/system.go +++ b/system.go @@ -3,7 +3,6 @@ package main import ( "bufio" "os" - "os/exec" "strings" "github.com/rwinkhart/go-boilerplate/front" @@ -13,7 +12,7 @@ import ( func system() { for { var doAll bool - choice := front.InputMenuGen("Tweak:", []string{"BACK", "ALL", "replace sudo with doas", "enable ipv6 privacy extensions", "enable ext4 fast_commit", "enable SysRq reboots", "disable kernel security mitigations", "unlock amdgpu tuning", "enable amd_pstate in passive mode (recommended for zenver2)"}) + choice := front.InputMenuGen("Tweak:", []string{"BACK", "ALL", "replace sudo with doas", "enable ipv6 privacy extensions", "enable SysRq reboots", "disable kernel security mitigations", "unlock amdgpu tuning", "enable amd_pstate in passive mode (recommended for zenver2)"}) switch choice { case 1: return @@ -22,69 +21,9 @@ func system() { fallthrough case 3: // TODO fix! - err := os.RemoveAll("/tmp/custom-base-devel/") - if err != nil { - other.PrintError("Failed to remove /tmp/custom-base-devel", 1) - } - writeFile("/etc/doas.conf", "permit persist keepenv :wheel as root\n", "root", 0644) - err = os.MkdirAll("/tmp/custom-base-devel", 0777) - if err != nil { - other.PrintError("Failed to create /tmp/custom-base-devel/", 1) - } - uid := getUID() - err = os.Chown("/tmp/custom-base-devel", uid, uid) - writeFile("/tmp/custom-base-devel/PKGBUILD", `pkgname=base-devel -pkgver=1 -pkgrel=2 -pkgdesc='Basic tools to build Arch Linux packages' -url='https://www.archlinux.org' -arch=('any') -license=('GPL') -options=('!debug') -depends=( - archlinux-keyring - autoconf - automake - binutils - bison - debugedit - fakeroot - file - findutils - flex - gawk - gcc - gettext - grep - groff - gzip - libtool - m4 - make - pacman - pacman-contrib - patch - pkgconf - sed - opendoas - texinfo - which -) - -# vim: ts=2 sw=2 et: -`, getUsername(), 0777) - cmd := exec.Command("sudo", "-u", getUsername(), "makepkg", "--dir", "/tmp/custom-base-devel") - err = cmd.Run() - if err != nil { - other.PrintError("Failed to build+install custom base-devel: "+err.Error(), 1) - } - managePackage("-U", "/tmp/custom-base-devel/base-devel-1-2-any.pkg.tar.xz") + writeFile(mountPoint+"/etc/doas.conf", "permit persist keepenv :wheel as root\n", "root", 0644) managePackage("-S", "opendoas") - cmd = exec.Command("pacman", "-R", "sudo", "qt-sudo", "octopi", "--noconfirm") - err = cmd.Run() - if err != nil { - other.PrintError("Failed to remove sudo", 1) - } + //chrootCommandRun([]string{"pacman", "-R", "sudo", "qt-sudo", "--noconfirm"}) if !doAll { break @@ -100,39 +39,27 @@ depends=( for _, nic := range nics { sysctlConf.WriteString("\nnet.ipv6.conf." + nic.Name() + ".use_tempaddr = 2") } - writeFile("/etc/sysctl.d/40-ipv6-priv-ext.conf", sysctlConf.String()+"\n", "root", 0644) + writeFile(mountPoint+"/etc/sysctl.d/40-ipv6-priv-ext.conf", sysctlConf.String()+"\n", "root", 0644) if !doAll { break } fallthrough case 5: - // TODO move to chroot! - cmd := exec.Command("tune2fs", "-O", "fast_commit", front.Input("/dev/driveparition:")) - err := cmd.Run() - if err != nil { - other.PrintError("Failed to enable fast_commit", 1) - } + writeFile(mountPoint+"/etc/sysctl.d/35-sysrq.conf", "kernel.sysrq = 244\n", "root", 0644) if !doAll { break } fallthrough case 6: - writeFile("/etc/sysctl.d/35-sysrq.conf", "kernel.sysrq = 244\n", "root", 0644) - - if !doAll { - break - } - fallthrough - case 7: addKernelParams([]string{"mitigations=off"}) if !doAll { break } fallthrough - case 8: + case 7: addKernelParams([]string{"amdgpu.ppfeaturemask=0xffffffff"}) if !doAll { @@ -146,7 +73,7 @@ depends=( } func addKernelParams(newParameters []string) { - f, err := os.Open("/boot/loader/entries/linux-cachyos.conf") + f, err := os.Open(mountPoint + "/boot/loader/entries/linux-cachyos.conf") if err != nil { other.PrintError("Failed to open systemd-boot entry", 1) } @@ -160,5 +87,5 @@ func addKernelParams(newParameters []string) { } } parameters = append(parameters, newParameters...) - writeFile("/boot/loader/entries/linux-cachyos.conf", "title Linux Cachyos\noptions root=UUID=e2ff600b-0202-4620-bed3-54d7a58414f8 "+strings.Join(parameters, " ")+"\nlinux /vmlinuz-linux-cachyos\ninitrd /initramfs-linux-cachyos.img\n", "root", 0700) + writeFile(mountPoint+"/boot/loader/entries/linux-cachyos.conf", "title Linux Cachyos\noptions root=UUID=e2ff600b-0202-4620-bed3-54d7a58414f8 "+strings.Join(parameters, " ")+"\nlinux /vmlinuz-linux-cachyos\ninitrd /initramfs-linux-cachyos.img\n", "root", 0700) } diff --git a/universalTweaks.go b/universalTweaks.go index 8f92ad6..6c0c2c9 100644 --- a/universalTweaks.go +++ b/universalTweaks.go @@ -2,26 +2,22 @@ package main import ( "os" - "os/exec" "github.com/rwinkhart/go-boilerplate/other" ) func universalTweaks() { // Lock root account - cmd := exec.Command("usermod", "--delete", "root") - cmd.Run() - cmd = exec.Command("usermod", "--lock", "root") - cmd.Run() + chrootCommandRun([]string{"passwd", "--delete", "root"}) + chrootCommandRun([]string{"usermod", "--lock", "root"}) // Add user to uucp group for non-root serial console usage - cmd = exec.Command("usermod", "-aG", "uucp", getUsername()) - cmd.Run() + chrootCommandRun([]string{"usermod", "-aG", "uucp", getUsername()}) // Force disable kernel watchdog (kernel parameters should do this as well) - f, err := os.Create("/etc/modprobe.d/blacklist.conf") + f, err := os.Create(mountPoint + "/etc/modprobe.d/blacklist.conf") if err != nil { - other.PrintError("Failed to create /etc/modprobe.d/blacklist.conf", 1) + other.PrintError("Failed to create "+mountPoint+"/etc/modprobe.d/blacklist.conf", 1) } f.WriteString("# Disable kernel watchdog\nblacklist iTCO_wdt") f.Close() diff --git a/utilities.go b/utilities.go index 8ac07b7..9b6b1a6 100644 --- a/utilities.go +++ b/utilities.go @@ -5,30 +5,20 @@ import ( "os/exec" "os/user" "strconv" + "strings" + mnt "github.com/moby/sys/mountinfo" "github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/go-boilerplate/other" ) func manageService(action, service string) { - cmd := exec.Command("systemctl", "daemon-reload") - err := cmd.Run() - if err != nil { - other.PrintError("Failed to perform daemon-reload", 1) - } - cmd = exec.Command("systemctl", action, service) - err = cmd.Run() - if err != nil { - other.PrintError("Failed to "+action+" "+service, 1) - } + chrootCommandRun([]string{"systemctl", "daemon-reload"}) + chrootCommandRun([]string{"systemctl", action, service}) } func managePackage(action, targetPackage string) { - cmd := exec.Command("pacman", action, targetPackage, "--noconfirm") - err := cmd.Run() - if err != nil { - other.PrintError("Failed to "+action+" "+targetPackage, 1) - } + chrootCommandRun([]string{"pacman", action, targetPackage, "--noconfirm"}) } func writeFile(path, data, owner string, perms os.FileMode) { @@ -64,3 +54,38 @@ func getUID() int { } return uid } + +func partitionIsMounted() bool { + isMounted, err := mnt.Mounted(mountPoint) + if err != nil { + other.PrintError("Failed to verify whether "+partitionR+" is mounted", 1) + } + return isMounted +} + +func mountPartition() { + if !partitionIsMounted() { + // root + cmd := exec.Command("mount", partitionR, mountPoint) + err := cmd.Run() + if err != nil { + other.PrintError("Failed to mount "+partitionR+" on "+mountPoint, 1) + } + + // boot + cmd = exec.Command("mount", partitionB, mountPoint+"/boot") + err = cmd.Run() + if err != nil { + other.PrintError("Failed to mount "+partitionB+" on "+mountPoint+"/boot", 1) + } + } +} + +func chrootCommandRun(args []string) { + args = append([]string{mountPoint}, args...) + cmd := exec.Command("arch-chroot", args...) + err := cmd.Run() + if err != nil { + other.PrintError("Failed to run \""+strings.Join(args[1:], " ")+"\" within chroot: "+err.Error(), 1) + } +}