Switch to chroot-based usage (run from installer ISO)

This commit is contained in:
2025-09-29 23:51:15 -04:00
parent 85b345d54c
commit 9cbabb58c9
12 changed files with 128 additions and 139 deletions
+1 -5
View File
@@ -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 - whichever one you don't need
- Firefox and language package - 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. As soon as the system is installed (prior to booting into it), 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.
+1 -8
View File
@@ -1,10 +1,7 @@
package main package main
import ( import (
"os/exec"
"github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/go-boilerplate/front"
"github.com/rwinkhart/go-boilerplate/other"
) )
func applications() { func applications() {
@@ -46,11 +43,7 @@ func applications() {
} }
fallthrough fallthrough
case 7: case 7:
cmd := exec.Command("pacman", "-S", "virt-manager", "qemu-desktop", "libvirt", "--noconfirm") chrootCommandRun([]string{"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)
}
manageService("enable", "libvirtd.service") manageService("enable", "libvirtd.service")
manageService("start", "libvirtd.service") manageService("start", "libvirtd.service")
// TODO manage user groups! // TODO manage user groups!
+9 -9
View File
@@ -19,39 +19,39 @@ func configurations() {
fallthrough fallthrough
case 3: case 3:
managePackage("-S", "wayland-protocols") managePackage("-S", "wayland-protocols")
writeFile("/home/"+getUsername()+"/.profile", shellProfile, getUsername(), 0644) writeFile(mountPoint+"/home/"+getUsername()+"/.profile", shellProfile, getUsername(), 0644)
if !doAll { if !doAll {
break break
} }
fallthrough fallthrough
case 4: case 4:
writeFile("/home/"+getUsername()+"/.zshrc", zshrc, getUsername(), 0644) writeFile(mountPoint+"/home/"+getUsername()+"/.zshrc", zshrc, getUsername(), 0644)
if !doAll { if !doAll {
break break
} }
fallthrough fallthrough
case 5: case 5:
writeFile("/etc/xdg/nvim/sysinit.vim", sysinitVim, "root", 0644) writeFile(mountPoint+"/etc/xdg/nvim/sysinit.vim", sysinitVim, "root", 0644)
if !doAll { if !doAll {
break break
} }
fallthrough fallthrough
case 6: case 6:
err := os.MkdirAll("/home/"+getUsername()+"/.config/zed", 0755) err := os.MkdirAll(mountPoint+"/home/"+getUsername()+"/.config/zed", 0755)
if err != nil { if err != nil {
other.PrintError("Failed to create zed config directory", 1) 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 { if !doAll {
break break
} }
fallthrough fallthrough
case 7: case 7:
writeFile("/etc/makepkg.conf", makepkg, "root", 0644) writeFile(mountPoint+"/etc/makepkg.conf", makepkg, "root", 0644)
if !doAll { if !doAll {
break break
@@ -59,9 +59,9 @@ func configurations() {
fallthrough fallthrough
case 8: case 8:
managePackage("-S", "pacman-contrib") managePackage("-S", "pacman-contrib")
writeFile("/etc/pacman.conf", pacman, "root", 0644) writeFile(mountPoint+"/etc/pacman.conf", pacman, "root", 0644)
writeFile("/etc/pacman.d/hooks/paccache-clean.hook", pacmanHookClean, "root", 0644) writeFile(mountPoint+"/etc/pacman.d/hooks/paccache-clean.hook", pacmanHookClean, "root", 0644)
writeFile("/etc/pacman.d/hooks/nvidia.hook", pacmanHookNvidia, "root", 0644) writeFile(mountPoint+"/etc/pacman.d/hooks/nvidia.hook", pacmanHookNvidia, "root", 0644)
} }
} }
} }
+2 -3
View File
@@ -22,12 +22,11 @@ func cosmic() {
"Tk" + version + "show_maximize": "false"} "Tk" + version + "show_maximize": "false"}
for key, value := range filenamesToValues { for key, value := range filenamesToValues {
filePath := "/home/" + getUsername() + "/.config/cosmic/com.system76.Cosmic" + key writeFile(mountPoint+"/home/"+getUsername()+"/.config/cosmic/com.system76.Cosmic"+key, value+"\n", getUsername(), 0755)
writeFile(filePath, value+"\n", getUsername(), 0755)
} }
// Replace SDDM with cosmic-greeter // 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("stop", "sddm.service")
manageService("disable", "sddm.service") manageService("disable", "sddm.service")
manageService("enable", "greetd.service") manageService("enable", "greetd.service")
+4 -1
View File
@@ -2,7 +2,10 @@ module cachy-post
go 1.25.1 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 ( require (
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
+2
View File
@@ -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 h1:EzlVj6R7Bxtl79Nl7R5zRcg6s+Cf2FAqGIzR4giWTQg=
github.com/rwinkhart/go-boilerplate v0.1.0/go.mod h1:cnzIF45I0FCOvE4YIB+26pLCUx2kWyY2llKYZruNaRY= github.com/rwinkhart/go-boilerplate v0.1.0/go.mod h1:cnzIF45I0FCOvE4YIB+26pLCUx2kWyY2llKYZruNaRY=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+31 -7
View File
@@ -1,13 +1,18 @@
package main package main
import ( import (
"fmt"
"os"
"os/user" "os/user"
"github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/go-boilerplate/front"
"github.com/rwinkhart/go-boilerplate/other" "github.com/rwinkhart/go-boilerplate/other"
) )
const mountPoint = "/mnt/cph"
var username string var username string
var partitionR, partitionB string
// TODO add mode to perform EVERYTHING // TODO add mode to perform EVERYTHING
func main() { func main() {
@@ -19,22 +24,41 @@ func main() {
other.PrintError("This utility must be run as root", 1) 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 { 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 { switch choice {
case 1: case 1:
universalTweaks() if partitionIsMounted() {
fmt.Println(partitionR + " is already mounted on " + mountPoint)
} else {
premount()
}
case 2: case 2:
system() mountPartition()
universalTweaks()
case 3: case 3:
mountPartition()
system()
case 4:
mountPartition()
removals() removals()
// TODO remove config/data // TODO remove config/data
case 4:
applications()
// TODO remove config/data for replacements
case 5: case 5:
configurations() mountPartition()
applications()
case 6: case 6:
mountPartition()
configurations()
case 7:
mountPartition()
cosmic() cosmic()
// TODO keyboard shortcuts for screenshot/sticky window/terminal // TODO keyboard shortcuts for screenshot/sticky window/terminal
// TODO window decoration position (not yet implemented?) // TODO window decoration position (not yet implemented?)
+24
View File
@@ -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)
}
}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
func removals() { func removals() {
for { for {
var doAll bool 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 var target string
switch choice { switch choice {
case 1: case 1:
+8 -81
View File
@@ -3,7 +3,6 @@ package main
import ( import (
"bufio" "bufio"
"os" "os"
"os/exec"
"strings" "strings"
"github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/go-boilerplate/front"
@@ -13,7 +12,7 @@ import (
func system() { func system() {
for { for {
var doAll bool 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 { switch choice {
case 1: case 1:
return return
@@ -22,69 +21,9 @@ func system() {
fallthrough fallthrough
case 3: case 3:
// TODO fix! // TODO fix!
err := os.RemoveAll("/tmp/custom-base-devel/") writeFile(mountPoint+"/etc/doas.conf", "permit persist keepenv :wheel as root\n", "root", 0644)
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")
managePackage("-S", "opendoas") managePackage("-S", "opendoas")
cmd = exec.Command("pacman", "-R", "sudo", "qt-sudo", "octopi", "--noconfirm") //chrootCommandRun([]string{"pacman", "-R", "sudo", "qt-sudo", "--noconfirm"})
err = cmd.Run()
if err != nil {
other.PrintError("Failed to remove sudo", 1)
}
if !doAll { if !doAll {
break break
@@ -100,39 +39,27 @@ depends=(
for _, nic := range nics { for _, nic := range nics {
sysctlConf.WriteString("\nnet.ipv6.conf." + nic.Name() + ".use_tempaddr = 2") 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 { if !doAll {
break break
} }
fallthrough fallthrough
case 5: case 5:
// TODO move to chroot! writeFile(mountPoint+"/etc/sysctl.d/35-sysrq.conf", "kernel.sysrq = 244\n", "root", 0644)
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)
}
if !doAll { if !doAll {
break break
} }
fallthrough fallthrough
case 6: 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"}) addKernelParams([]string{"mitigations=off"})
if !doAll { if !doAll {
break break
} }
fallthrough fallthrough
case 8: case 7:
addKernelParams([]string{"amdgpu.ppfeaturemask=0xffffffff"}) addKernelParams([]string{"amdgpu.ppfeaturemask=0xffffffff"})
if !doAll { if !doAll {
@@ -146,7 +73,7 @@ depends=(
} }
func addKernelParams(newParameters []string) { 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 { if err != nil {
other.PrintError("Failed to open systemd-boot entry", 1) other.PrintError("Failed to open systemd-boot entry", 1)
} }
@@ -160,5 +87,5 @@ func addKernelParams(newParameters []string) {
} }
} }
parameters = append(parameters, newParameters...) 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)
} }
+5 -9
View File
@@ -2,26 +2,22 @@ package main
import ( import (
"os" "os"
"os/exec"
"github.com/rwinkhart/go-boilerplate/other" "github.com/rwinkhart/go-boilerplate/other"
) )
func universalTweaks() { func universalTweaks() {
// Lock root account // Lock root account
cmd := exec.Command("usermod", "--delete", "root") chrootCommandRun([]string{"passwd", "--delete", "root"})
cmd.Run() chrootCommandRun([]string{"usermod", "--lock", "root"})
cmd = exec.Command("usermod", "--lock", "root")
cmd.Run()
// Add user to uucp group for non-root serial console usage // Add user to uucp group for non-root serial console usage
cmd = exec.Command("usermod", "-aG", "uucp", getUsername()) chrootCommandRun([]string{"usermod", "-aG", "uucp", getUsername()})
cmd.Run()
// Force disable kernel watchdog (kernel parameters should do this as well) // 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 { 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.WriteString("# Disable kernel watchdog\nblacklist iTCO_wdt")
f.Close() f.Close()
+40 -15
View File
@@ -5,30 +5,20 @@ import (
"os/exec" "os/exec"
"os/user" "os/user"
"strconv" "strconv"
"strings"
mnt "github.com/moby/sys/mountinfo"
"github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/go-boilerplate/front"
"github.com/rwinkhart/go-boilerplate/other" "github.com/rwinkhart/go-boilerplate/other"
) )
func manageService(action, service string) { func manageService(action, service string) {
cmd := exec.Command("systemctl", "daemon-reload") chrootCommandRun([]string{"systemctl", "daemon-reload"})
err := cmd.Run() chrootCommandRun([]string{"systemctl", action, service})
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)
}
} }
func managePackage(action, targetPackage string) { func managePackage(action, targetPackage string) {
cmd := exec.Command("pacman", action, targetPackage, "--noconfirm") chrootCommandRun([]string{"pacman", action, targetPackage, "--noconfirm"})
err := cmd.Run()
if err != nil {
other.PrintError("Failed to "+action+" "+targetPackage, 1)
}
} }
func writeFile(path, data, owner string, perms os.FileMode) { func writeFile(path, data, owner string, perms os.FileMode) {
@@ -64,3 +54,38 @@ func getUID() int {
} }
return uid 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)
}
}