{
abroot = cmdr.NewApp("abroot", version, fs)
return abroot
}
{
root := cmdr.NewCommand(
abroot.Trans("abroot.use"),
abroot.Trans("abroot.long"),
abroot.Trans("abroot.short"),
nil).
WithPersistentBoolFlag(
cmdr.NewBoolFlag(
verboseFlag,
"V",
abroot.Trans("abroot.verboseFlag"),
false))
root.Version = version
return root
}
{
cmd := cmdr.NewCommand(
"status",
abroot.Trans("status.long"),
abroot.Trans("status.short"),
func(cmd *cobra.Command, args []string) error {
err := status(cmd, args)
if err != nil {
os.Exit(1)
}
return nil
},
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"json",
"j",
abroot.Trans("status.jsonFlag"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"dump",
"d",
abroot.Trans("status.dumpFlag"),
false))
cmd.Example = "abroot status"
return cmd
}
{
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("status.rootRequired"))
return nil
}
jsonFlag, err := cmd.Flags().GetBool("json")
if err != nil {
return err
}
dumpFlag, err := cmd.Flags().GetBool("dump")
if err != nil {
return err
}
a := core.NewABRootManager()
present, err := a.GetPresent()
if err != nil {
return err
}
future, err := a.GetFuture()
if err != nil {
return err
}
specs := core.GetPCSpecs()
abImage, err := core.NewABImageFromRoot()
if err != nil {
return err
}
kargs, err := core.KargsRead()
if err != nil {
return err
}
pkgMngAgreementStatus := false
pkgMng, err := core.NewPackageManager(false)
if err != nil {
return err
}
if pkgMng.Status == core.PKG_MNG_REQ_AGREEMENT {
err = pkgMng.CheckStatus()
pkgMngAgreementStatus = err == nil
}
pkgsAdd, err := pkgMng.GetAddPackages()
if err != nil {
return err
}
pkgsRm, err := pkgMng.GetRemovePackages()
if err != nil {
return err
}
unstagedAdded, unstagedRemoved, err := pkgMng.GetUnstagedPackages("/")
if err != nil {
return err
}
pkgsUnstg := append(unstagedAdded, unstagedRemoved...)
if jsonFlag || dumpFlag {
type status struct {
Present string `json:"present"`
Future string `json:"future"`
CPU string `json:"cpu"`
GPU []string `json:"gpu"`
Memory string `json:"memory"`
ABImage core.ABImage `json:"abimage"`
Kargs string `json:"kargs"`
PkgsAdd []string `json:"pkgsAdd"`
PkgsRm []string `json:"pkgsRm"`
PkgsUnstg []string `json:"pkgsUnstg"`
PkgMngStatus int `json:"pkgMngStatus"`
PkgMngAgreement bool `json:"pkgMngAg"`
}
s := status{
Present: present.Label,
Future: future.Label,
CPU: specs.CPU,
GPU: specs.GPU,
Memory: specs.Memory,
ABImage: *abImage,
Kargs: kargs,
PkgsAdd: pkgsAdd,
PkgsRm: pkgsRm,
PkgsUnstg: pkgsUnstg,
PkgMngStatus: settings.Cnf.IPkgMngStatus,
PkgMngAgreement: pkgMngAgreementStatus,
}
b, err := json.Marshal(s)
if err != nil {
return err
}
if jsonFlag {
fmt.Println(string(b))
return nil
}
tarballPath := fmt.Sprintf("/tmp/abroot-status-%s.tar.gz", uuid.New().String())
tarballFile, err := os.Create(tarballPath)
if err != nil {
return err
}
defer tarballFile.Close()
gzipWriter := gzip.NewWriter(tarballFile)
defer gzipWriter.Close()
tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()
tarHeader := &tar.Header{
Name: "status.json",
Mode: 0o644,
Size: int64(len(b)),
}
err = tarWriter.WriteHeader(tarHeader)
if err != nil {
return err
}
_, err = tarWriter.Write(b)
if err != nil {
return err
}
err = filepath.Walk("/var/log/", func(path string, info os.FileInfo, err error) error {
if strings.Contains(path, "abroot.log") {
relPath, err := filepath.Rel("/var/log/", path)
if err != nil {
return err
}
tarHeader := &tar.Header{
Name: filepath.Join("logs", relPath),
Mode: 0o644,
Size: info.Size(),
}
err = tarWriter.WriteHeader(tarHeader)
if err != nil {
return err
}
logFile, err := os.Open(path)
if err != nil {
return err
}
defer logFile.Close()
_, err = io.Copy(tarWriter, logFile)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
cmdr.Info.Printf(abroot.Trans("status.dumpMsg"), tarballPath)
return nil
}
formattedGPU := ""
for _, gpu := range specs.GPU {
formattedGPU += fmt.Sprintf("\n\t\t- %s", gpu)
}
unstagedAlert := ""
if len(pkgsUnstg) > 0 {
unstagedAlert = abroot.Trans("status.unstagedFoundMsg", len(pkgsUnstg))
}
presentMark, futureMark, err := getCurrentlyBootedPartition(a)
if err != nil {
return err
}
// ABRoot partitions:
cmdr.Bold.Println(abroot.Trans("status.partitions.title"))
cmdr.BulletList.WithItems([]cmdr.BulletListItem{
{Level: 1, Text: abroot.Trans("status.partitions.present", present.Label, presentMark)},
{Level: 1, Text: abroot.Trans("status.partitions.future", future.Label, futureMark)},
}).Render()
// Device Specification:
cmdr.Bold.Println(abroot.Trans("status.specs.title"))
cmdr.BulletList.WithItems([]cmdr.BulletListItem{
{Level: 1, Text: abroot.Trans("status.specs.cpu", specs.CPU)},
{Level: 1, Text: abroot.Trans("status.specs.gpu", specs.GPU)},
{Level: 1, Text: abroot.Trans("status.specs.memory", specs.Memory)},
}).Render()
// ABImage:
cmdr.Bold.Println(abroot.Trans("status.abimage.title"))
cmdr.BulletList.WithItems([]cmdr.BulletListItem{
{Level: 1, Text: abroot.Trans("status.abimage.digest", abImage.Digest)},
{Level: 1, Text: abroot.Trans("status.abimage.timestamp", abImage.Timestamp.Format("2006-01-02 15:04:05"))},
{Level: 1, Text: abroot.Trans("status.abimage.image", abImage.Image)},
}).Render()
// Kernel Arguments: ...
cmdr.Bold.Printf(abroot.Trans("status.kargs") + " ")
cmdr.FgDefault.Println(kargs)
cmdr.FgDefault.Println()
// Packages:
cmdr.Bold.Println(abroot.Trans("status.packages.title"))
cmdr.BulletList.WithItems([]cmdr.BulletListItem{
{Level: 1, Text: abroot.Trans("status.packages.added", strings.Join(pkgsAdd, ", "))},
{Level: 1, Text: abroot.Trans("status.packages.removed", strings.Join(pkgsRm, ", "))},
{Level: 1, Text: abroot.Trans("status.packages.unstaged", strings.Join(pkgsUnstg, ", "), unstagedAlert)},
}).Render()
// Package Agreement: ...
cmdr.Bold.Print(abroot.Trans("status.agreementStatus") + " ")
cmdr.FgDefault.Println(pkgMngAgreementStatus)
return nil
}
{
bootPart, err := a.GetBoot()
if err != nil {
return "", "", err
}
tmpBootMount := "/run/abroot/tmp-boot-mount-status/"
err = os.MkdirAll(tmpBootMount, 0o755)
if err != nil {
return "", "", err
}
err = bootPart.Mount(tmpBootMount)
if err != nil {
return "", "", err
}
defer bootPart.Unmount()
g, err := core.NewGrub(bootPart)
if err != nil {
return "", "", err
}
isPresent, err := g.IsBootedIntoPresentRoot()
if err != nil {
return "", "", err
}
presentMark := ""
futureMark := ""
if isPresent {
presentMark = " ✓"
} else {
futureMark = " ✓"
}
return presentMark, futureMark, nil
}
{
cmd := cmdr.NewCommand(
"update-initramfs",
abroot.Trans("updateInitramfs.long"),
abroot.Trans("updateInitramfs.short"),
func(cmd *cobra.Command, args []string) error {
err := updateInitramfs(cmd, args)
if err != nil {
os.Exit(1)
}
return nil
},
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"dry-run",
"d",
abroot.Trans("updateInitramfs.dryRunFlag"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"delete-old-system",
"",
abroot.Trans("upgrade.deleteOld"),
false))
cmd.Example = "abroot update-initramfs"
return cmd
}
{
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("updateInitramfs.rootRequired"))
return nil
}
dryRun, err := cmd.Flags().GetBool("dry-run")
if err != nil {
cmdr.Error.Println(err)
return err
}
freeSpace, err := cmd.Flags().GetBool("delete-old-system")
if err != nil {
cmdr.Error.Println(err)
return err
}
aBsys, err := core.NewABSystem()
if err != nil {
cmdr.Error.Println(err)
return err
}
if dryRun {
err = aBsys.RunOperation(core.DRY_RUN_INITRAMFS, freeSpace)
} else {
err = aBsys.RunOperation(core.INITRAMFS, freeSpace)
}
if err != nil {
cmdr.Error.Printf(abroot.Trans("updateInitramfs.updateFailed"), err)
return err
}
cmdr.Info.Println(abroot.Trans("updateInitramfs.updateSuccess"))
return nil
}
{
cmd := cmdr.NewCommand(
"upgrade",
abroot.Trans("upgrade.long"),
abroot.Trans("upgrade.short"),
func(cmd *cobra.Command, args []string) error {
err := upgrade(cmd, args)
if err != nil {
os.Exit(1)
}
return nil
},
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"check-only",
"c",
abroot.Trans("upgrade.checkOnlyFlag"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"dry-run",
"d",
abroot.Trans("upgrade.dryRunFlag"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"force",
"f",
abroot.Trans("upgrade.forceFlag"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"delete-old-system",
"",
abroot.Trans("upgrade.deleteOld"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"cancel",
"",
abroot.Trans("upgrade.cancel"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"unblock",
"",
abroot.Trans("upgrade.unblock"),
false))
cmd.Example = "abroot upgrade"
return cmd
}
{
checkOnly, err := cmd.Flags().GetBool("check-only")
if err != nil {
cmdr.Error.Println(err)
return err
}
dryRun, err := cmd.Flags().GetBool("dry-run")
if err != nil {
cmdr.Error.Println(err)
return err
}
freeSpace, err := cmd.Flags().GetBool("delete-old-system")
if err != nil {
cmdr.Error.Println(err)
return err
}
block, err := cmd.Flags().GetBool("cancel")
if err != nil {
cmdr.Error.Println(err)
return err
}
unblock, err := cmd.Flags().GetBool("unblock")
if err != nil {
cmdr.Error.Println(err)
return err
}
if block {
if dryRun {
cmdr.Info.Println("skipped blocking due to dry run")
return nil
}
err := core.MakeStopRequest()
if err != nil {
cmdr.Error.Println(err)
}
return err
}
if unblock {
if dryRun {
cmdr.Info.Println("can't use unblock and dryrun together")
return nil
}
err = core.CancelStopRequest()
if err != nil {
cmdr.Warning.Println("could not unblock operations:", err)
}
}
aBsys, err := core.NewABSystem()
if err != nil {
cmdr.Error.Println(err)
return err
}
if checkOnly {
_, raw := os.LookupEnv("ABROOT_JSON_OUTPUT")
if !raw {
cmdr.Info.Println(abroot.Trans("upgrade.checkingSystemUpdate"))
}
// Check for image updates
newDigest, res, err := aBsys.CheckUpdate()
if err != nil {
cmdr.Error.Println(err)
return err
}
sysAdded, sysUpgraded, sysDowngraded, sysRemoved := []diff.PackageDiff{}, []diff.PackageDiff{}, []diff.PackageDiff{}, []diff.PackageDiff{}
if res {
if !raw {
cmdr.Info.Println(abroot.Trans("upgrade.systemUpdateAvailable"))
}
sysAdded, sysUpgraded, sysDowngraded, sysRemoved, err = core.BaseImagePackageDiff(aBsys.CurImage.Digest, newDigest)
if err != nil {
return err
}
if !raw {
err = renderPackageDiff(sysAdded, sysUpgraded, sysDowngraded, sysRemoved)
if err != nil {
return err
}
}
} else if !raw {
cmdr.Info.Println(abroot.Trans("upgrade.noUpdateAvailable"))
}
// Check for package updates
if !raw {
cmdr.Info.Println(abroot.Trans("upgrade.checkingPackageUpdate"))
}
ovlAdded, ovlUpgraded, ovlDowngraded, ovlRemoved, err := core.OverlayPackageDiff()
if err != nil {
return err
}
sumChanges := len(ovlAdded) + len(ovlUpgraded) + len(ovlDowngraded) + len(ovlRemoved)
if sumChanges == 0 && !raw {
cmdr.Info.Println(abroot.Trans("upgrade.noUpdateAvailable"))
} else if !raw {
cmdr.Info.Sprintf(abroot.Trans("upgrade.packageUpdateAvailable"), sumChanges)
err = renderPackageDiff(ovlAdded, ovlUpgraded, ovlDowngraded, ovlRemoved)
if err != nil {
return err
}
}
if raw {
newDigestIfHasUpdate := ""
if res {
newDigestIfHasUpdate = newDigest
}
out, err := json.Marshal(map[string]any{
"hasUpdate": res,
"newDigest": newDigestIfHasUpdate,
"systemPackageDiff": map[string][]diff.PackageDiff{
"added": sysAdded,
"upgraded": sysUpgraded,
"downgraded": sysDowngraded,
"removed": sysRemoved,
},
"overlayPackageDiff": map[string][]diff.PackageDiff{
"added": ovlAdded,
"upgraded": ovlUpgraded,
"downgraded": ovlDowngraded,
"removed": ovlRemoved,
},
})
if err != nil {
cmdr.Error.Println(err)
}
fmt.Println(string(out))
}
if !res && sumChanges == 0 {
os.Exit(1) // No update available
} else {
os.Exit(0) // Update available
}
}
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("upgrade.rootRequired"))
return nil
}
force, err := cmd.Flags().GetBool("force")
if err != nil {
cmdr.Error.Println(err)
return err
}
var operation core.ABSystemOperation
if force {
operation = core.FORCE_UPGRADE
} else if dryRun {
operation = core.DRY_RUN_UPGRADE
} else {
operation = core.UPGRADE
}
cmdr.Info.Println(abroot.Trans("upgrade.checkingSystemUpdate"))
err = aBsys.RunOperation(operation, freeSpace)
if err != nil {
if err == core.ErrNoUpdate {
cmdr.Info.Println(abroot.Trans("upgrade.noUpdateAvailable"))
return err
}
cmdr.Error.Println(err)
return err
}
if dryRun {
cmdr.Info.Println(abroot.Trans("upgrade.dryRunSuccess"))
}
cmdr.Info.Println(abroot.Trans("upgrade.success"))
os.Exit(0)
return nil
}
{
pkgFmt := "%s '%s' -> '%s'"
// Calculate largest string for proper alignment
largestPkgName := 0
for _, pkgSet := range [][]diff.PackageDiff{added, upgraded, downgraded, removed} {
for _, pkg := range pkgSet {
if len(pkg.Name) > largestPkgName {
largestPkgName = len(pkg.Name)
}
}
}
for _, pkgSet := range []struct {
Set []diff.PackageDiff
Header string
Color cmdr.Color
}{
{added, abroot.Trans("upgrade.added"), cmdr.FgGreen},
{upgraded, abroot.Trans("upgrade.upgraded"), cmdr.FgBlue},
{downgraded, abroot.Trans("upgrade.downgraded"), cmdr.FgYellow},
{removed, abroot.Trans("upgrade.removed"), cmdr.FgRed},
} {
cmdr.NewStyle(cmdr.Bold, pkgSet.Color).Println(pkgSet.Header + ":")
bulletItems := []cmdr.BulletListItem{}
for _, pkg := range pkgSet.Set {
bulletItems = append(bulletItems, cmdr.BulletListItem{
Level: 1,
Text: fmt.Sprintf(pkgFmt, pkg.Name+strings.Repeat(" ", largestPkgName-len(pkg.Name)), pkg.PreviousVersion, pkg.NewVersion),
})
}
err := cmdr.BulletList.WithItems(bulletItems).Render()
if err != nil {
return err
}
}
return nil
}
{
cmd := cmdr.NewCommand(
"config-editor",
abroot.Trans("cnf.long"),
abroot.Trans("cnf.short"),
func(cmd *cobra.Command, args []string) error {
err := cnf(cmd, args)
if err != nil {
os.Exit(1)
}
return nil
},
)
return cmd
}
{
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("cnf.rootRequired"))
return nil
}
result, err := core.ConfEdit()
switch result {
case core.CONF_CHANGED:
cmdr.Info.Println(abroot.Trans("cnf.changed"))
case core.CONF_UNCHANGED:
cmdr.Info.Println(abroot.Trans("cnf.unchanged"))
case core.CONF_FAILED:
cmdr.Error.Println(abroot.Trans("cnf.failed", err))
}
return nil
}
{
cmd := cmdr.NewCommand(
"rollback",
abroot.Trans("rollback.long"),
abroot.Trans("rollback.short"),
func(cmd *cobra.Command, args []string) error {
err := rollback(cmd, args)
if err != nil {
os.Exit(1)
}
return nil
},
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"check-only",
"c",
abroot.Trans("rollback.checkOnlyFlag"),
false))
cmd.Example = "abroot rollback"
return cmd
}
{
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("rollback.rootRequired"))
return nil
}
checkOnly, err := cmd.Flags().GetBool("check-only")
if err != nil {
cmdr.Error.Println(err)
return err
}
aBsys, err := core.NewABSystem()
if err != nil {
cmdr.Error.Println(err)
return err
}
response, err := aBsys.Rollback(checkOnly)
if err != nil {
cmdr.Error.Println(err)
os.Exit(2)
return err
}
switch response {
case core.ROLLBACK_RES_YES:
// NOTE: the following strings could lead to misinterpretation, with
// "can" and "cannot", we don't mean "is it possible to rollback?",
// but "is it necessary to rollback?"
cmdr.Info.Println(abroot.Trans("rollback.canRollback"))
os.Exit(0)
case core.ROLLBACK_RES_NO:
cmdr.Info.Println(abroot.Trans("rollback.cannotRollback"))
os.Exit(1)
case core.ROLLBACK_UNNECESSARY:
cmdr.Info.Println(abroot.Trans("rollback.rollbackUnnecessary"))
os.Exit(0)
case core.ROLLBACK_SUCCESS:
cmdr.Info.Println(abroot.Trans("rollback.rollbackSuccess"))
os.Exit(0)
}
return nil
}
{
cmd := cmdr.NewCommand(
"pkg add|remove|list|apply",
abroot.Trans("pkg.long"),
abroot.Trans("pkg.short"),
func(cmd *cobra.Command, args []string) error {
err := pkg(cmd, args)
if err != nil {
os.Exit(1)
}
return nil
},
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"dry-run",
"d",
abroot.Trans("pkg.dryRunFlag"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"force-enable-user-agreement",
"f",
abroot.Trans("pkg.forceEnableUserAgreementFlag"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"force-apply",
"",
abroot.Trans("pkg.forceApply"),
false))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"delete-old-system",
"",
abroot.Trans("upgrade.deleteOld"),
false))
cmd.Args = cobra.MinimumNArgs(1)
cmd.ValidArgs = validPkgArgs
cmd.Example = "abroot pkg add <pkg>"
return cmd
}
{
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("pkg.rootRequired"))
return nil
}
dryRun, err := cmd.Flags().GetBool("dry-run")
if err != nil {
cmdr.Error.Println(err)
return err
}
freeSpace, err := cmd.Flags().GetBool("delete-old-system")
if err != nil {
cmdr.Error.Println(err)
return err
}
forceEnableUserAgreement, err := cmd.Flags().GetBool("force-enable-user-agreement")
if err != nil {
cmdr.Error.Println(err)
return err
}
forceApply, err := cmd.Flags().GetBool("force-apply")
if err != nil {
cmdr.Error.Println(err)
return err
}
pkgM, err := core.NewPackageManager(false)
if err != nil {
cmdr.Error.Println(abroot.Trans("pkg.failedGettingPkgManagerInstance", err))
return err
}
// Check for user agreement, here we could simply call the CheckStatus
// function which also checks if the package manager is enabled or not
// since this pkg command is not even added to the root command if the
// package manager is disabled, but we want to be explicit here to avoid
// potential hard to debug errors in the future in weird development
// scenarios. Yeah, trust me, I've been there.
if pkgM.Status == core.PKG_MNG_REQ_AGREEMENT {
err = pkgM.CheckStatus()
if err != nil {
if !forceEnableUserAgreement {
cmdr.Info.Println(abroot.Trans("pkg.agreementMsg"))
reader := bufio.NewReader(os.Stdin)
answer, _ := reader.ReadString('\n')
answer = strings.TrimSpace(answer)
if answer == "y" || answer == "Y" {
err := pkgM.AcceptUserAgreement()
if err != nil {
cmdr.Error.Println(abroot.Trans("pkg.agreementSignFailed"), err)
return err
}
} else {
cmdr.Info.Println(abroot.Trans("pkg.agreementDeclined"))
return nil
}
} else {
err := pkgM.AcceptUserAgreement()
if err != nil {
cmdr.Error.Println(abroot.Trans("pkg.agreementSignFailed"), err)
return err
}
}
}
}
switch args[0] {
case "add":
if len(args) < 2 {
return errors.New(abroot.Trans("pkg.noPackageNameProvided"))
}
for _, pkg := range args[1:] {
err := pkgM.Add(pkg)
if err != nil {
cmdr.Error.Println(err)
return err
}
}
cmdr.Info.Printf(abroot.Trans("pkg.addedMsg"), strings.Join(args[1:], ", "))
case "remove":
if len(args) < 2 {
return errors.New(abroot.Trans("pkg.noPackageNameProvided"))
}
for _, pkg := range args[1:] {
err := pkgM.Remove(pkg)
if err != nil {
cmdr.Error.Println(err)
return err
}
}
cmdr.Info.Printf(abroot.Trans("pkg.removedMsg"), strings.Join(args[1:], ", "))
case "list":
added, err := pkgM.GetAddPackagesString("\n")
if err != nil {
cmdr.Error.Println(err)
return err
}
removed, err := pkgM.GetRemovePackagesString("\n")
if err != nil {
cmdr.Error.Println(err)
return err
}
cmdr.Info.Printf(abroot.Trans("pkg.listMsg"), added, removed)
return nil
case "apply":
unstagedAdded, unstagedRemoved, err := pkgM.GetUnstagedPackages("/")
if err != nil {
cmdr.Error.Println(err)
return err
}
if !forceApply && len(unstagedAdded) == 0 && len(unstagedRemoved) == 0 {
cmdr.Info.Println(abroot.Trans("pkg.noChanges"))
return nil
}
aBsys, err := core.NewABSystem()
if err != nil {
cmdr.Error.Println(err)
return err
}
if dryRun {
err = aBsys.RunOperation(core.DRY_RUN_APPLY, freeSpace)
} else {
err = aBsys.RunOperation(core.APPLY, freeSpace)
}
if err != nil {
cmdr.Error.Printf(abroot.Trans("pkg.applyFailed"), err)
return err
}
cmdr.Info.Println(abroot.Trans("pkg.applySuccess"))
default:
cmdr.Error.Println(abroot.Trans("pkg.unknownCommand", args[0]))
return nil
}
return nil
}
{
cmd := cmdr.NewCommand(
"rebase <name>",
abroot.Trans("rebase.long"),
abroot.Trans("rebase.short"),
rebase,
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"dry-run",
"d",
abroot.Trans("rebase.dryRunFlag"),
false,
))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"remove-packages",
"r",
abroot.Trans("rebase.removePackagesShort"),
false,
))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"keep-packages",
"k",
abroot.Trans("rebase.keepPackages"),
false,
))
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"rebase-only",
"n",
abroot.Trans("rebase.rebaseOnly"),
false,
))
cmd.Args = cobra.ExactArgs(1)
cmd.Example = "abroot rebase ghcr.io/vanilla-os/desktop:main"
return cmd
}
{
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("rebase.rootRequired"))
return nil
}
dryRun, err := cmd.Flags().GetBool("dry-run")
if err != nil {
cmdr.Error.Println(err)
return err
}
if err != nil {
cmdr.Error.Println(err)
return err
}
removePackages, err := cmd.Flags().GetBool("remove-packages")
if err != nil {
cmdr.Error.Println(err)
return err
}
keepPackages, err := cmd.Flags().GetBool("keep-packages")
if err != nil {
cmdr.Error.Println(err)
return err
}
if removePackages && keepPackages {
flagError := errors.New(abroot.Trans("rebase.flagError"))
cmdr.Error.Println(flagError)
return err
}
name := args[0]
abSys, err := core.NewABSystem()
if err != nil {
cmdr.Error.Println(err)
return err
}
pkgM, err := core.NewPackageManager(dryRun)
if pkgM.Status == core.PKG_MNG_ENABLED {
var removePackagesPrompt string
if !keepPackages && !removePackages {
cmdr.Info.Print(abroot.Trans("rebase.removePackagesLong"), " (y/N) ", " ")
fmt.Scanln(&removePackagesPrompt)
}
if strings.Contains(removePackagesPrompt, "y") || removePackages {
addedPackages, err := pkgM.GetAddPackages()
if err != nil {
return err
}
core.PrintVerboseInfo("cmd.Rebase", "Removing packages: ", addedPackages)
if !dryRun {
for _, v := range addedPackages {
pkgM.Remove(v)
}
}
cmdr.Info.Println(abroot.Trans("rebase.pkgRemoveSuccess"))
}
}
err = abSys.Rebase(name, dryRun)
if err != nil {
cmdr.Error.Println(err)
return err
}
if dryRun {
cmdr.Info.Println(abroot.Trans("rebase.dryRunSuccess"))
}
rebaseOnly, err := cmd.Flags().GetBool("rebase-only")
if err != nil {
cmdr.Error.Println(err)
return err
}
if rebaseOnly {
cmdr.Info.Println(abroot.Trans("rebase.success"))
return nil
}
cmdr.Info.Println(abroot.Trans("rebase.successUpdate"))
return abSys.RunOperation("UPGRADE", false)
}
{
return "the /var disk " + e.passedDisk + " does not exist"
}
{
return "the var partition is not encrypted"
}
{
cmd := cmdr.NewCommand(
"unlock-var",
"",
"",
unlockVarCmd,
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"dry-run",
"d",
"perform a dry run of the operation",
false,
),
)
// this is just meant for compatability with old Installations
cmd.WithStringFlag(
cmdr.NewStringFlag(
"var-disk",
"m",
"pass /var disk directly instead of reading from configuration",
"",
),
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"check-encrypted",
"c",
"check if drive is encrypted and return",
false,
),
)
cmd.Example = "abroot unlock-var"
cmd.Hidden = true
return cmd
}
helper function which only returns syntax errors and prints other ones
{
err := unlockVar(cmd, args)
if err != nil {
cmdr.Error.Println(err)
os.Exit(1)
return nil
}
return nil
}
{
if !core.RootCheck(false) {
cmdr.Error.Println("You must be root to run this command.")
os.Exit(2)
return nil
}
varDisk, err := cmd.Flags().GetString("var-disk")
if err != nil {
return err
}
check_only, err := cmd.Flags().GetBool("check-encrypted")
if err != nil {
return err
}
_, err = os.Stat(filepath.Join("/dev/disk/by-label/", settings.Cnf.PartLabelVar))
if err == nil || !errors.Is(err, os.ErrNotExist) {
return &NotEncryptedError{}
}
if check_only {
cmdr.Info.Println("The var partition is encrypted.")
return nil
}
if varDisk == "" {
if settings.Cnf.PartCryptVar == "" {
cmdr.Error.Println("Encrypted var partition not found in configuration.")
os.Exit(3)
return nil
}
varDisk = settings.Cnf.PartCryptVar
}
dryRun, err := cmd.Flags().GetBool("dry-run")
if err != nil {
return err
}
partitions, err := core.NewDiskManager().GetPartitions("")
if err != nil {
return err
}
var varLuksPart core.Partition
foundPart := false
for _, partition := range partitions {
devName := "/dev/"
if partition.IsDevMapper() {
devName += "mapper/"
}
devName += partition.Device
if devName == varDisk {
varLuksPart = partition
foundPart = true
break
}
}
if !foundPart {
return &VarInvalidError{varDisk}
}
uuid := varLuksPart.Uuid
cmdr.FgDefault.Println("unlocking", varDisk)
if dryRun {
cmdr.Info.Println("Dry run complete.")
} else {
cryptsetupCmd := exec.Command("/usr/sbin/cryptsetup", "luksOpen", varDisk, "luks-"+uuid)
cryptsetupCmd.Stdin = os.Stdin
cryptsetupCmd.Stderr = os.Stderr
cryptsetupCmd.Stdout = os.Stdout
err := cryptsetupCmd.Run()
if err != nil {
return err
}
cmdr.Info.Println("The system mounts have been performed successfully.")
}
return nil
}
{
cmd := cmdr.NewCommand(
"kargs edit|show",
abroot.Trans("kargs.long"),
abroot.Trans("kargs.short"),
func(cmd *cobra.Command, args []string) error {
err := kargs(cmd, args)
if err != nil {
os.Exit(1)
}
return nil
},
)
cmd.Args = cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs)
cmd.ValidArgs = validKargsArgs
cmd.Example = "abroot kargs edit"
return cmd
}
{
if !core.RootCheck(false) {
cmdr.Error.Println(abroot.Trans("kargs.rootRequired"))
return nil
}
switch args[0] {
case "edit":
changed, err := core.KargsEdit()
if err != nil {
cmdr.Error.Println(err)
return err
}
if !changed {
cmdr.Info.Println(abroot.Trans("kargs.notChanged"))
return nil
}
aBsys, err := core.NewABSystem()
if err != nil {
cmdr.Error.Println(err)
return err
}
err = aBsys.RunOperation(core.APPLY, false)
if err != nil {
cmdr.Error.Println(abroot.Trans("pkg.applyFailed"))
return err
}
case "show":
kargsStr, err := core.KargsRead()
if err != nil {
cmdr.Error.Println(err)
return err
}
cmdr.Info.Println(kargsStr)
default:
return errors.New(abroot.Trans("kargs.unknownCommand", args[0]))
}
return nil
}
{
return fmt.Sprintf("device %s has an unsupported layout", e.Device)
}
{
return fmt.Sprintf("partition %s could not be found", e.Partition)
}
{
cmd := cmdr.NewCommand(
"mount-sys",
"",
"",
mountSysCmd,
)
cmd.WithBoolFlag(
cmdr.NewBoolFlag(
"dry-run",
"d",
"perform a dry run of the operation",
false,
),
)
cmd.Example = "abroot mount-sys"
cmd.Hidden = true
return cmd
}
helper function which only returns syntax errors and prints other ones
{
err := mountSys(cmd, args)
if err != nil {
cmdr.Error.Println(err)
os.Exit(1)
}
return nil
}
{
if !core.RootCheck(false) {
cmdr.Error.Println("This operation requires root.")
return nil
}
dryRun, err := cmd.Flags().GetBool("dry-run")
if err != nil {
return err
}
manager := core.NewABRootManager()
present, err := manager.GetPresent()
if err != nil {
return err
}
// remount as writeable
if !dryRun {
err := syscall.Mount("/", "/", "", syscall.MS_REMOUNT, "")
if err != nil {
cmdr.Error.Println("failed to remount root", err)
}
}
err = mountVar(manager.VarPartition, dryRun)
if err != nil {
cmdr.Error.Println(err)
os.Exit(5)
}
if !dryRun {
err = core.RepairRootIntegrity("/")
if err != nil {
cmdr.Error.Println(err)
os.Exit(4)
}
}
err = mountBindMounts(dryRun)
if err != nil {
cmdr.Error.Println(err)
os.Exit(6)
}
if present.Label == "" {
return &PartNotFoundError{"current root"}
}
err = mountOverlayMounts(present.Label, dryRun)
if err != nil {
cmdr.Error.Println(err)
os.Exit(7)
}
if present.Uuid == "" {
return &PartNotFoundError{"current root"}
}
err = adjustFstab(present.Uuid, dryRun)
if err != nil {
cmdr.Error.Println(err)
os.Exit(8)
}
if dryRun {
cmdr.Info.Println("Dry run complete.")
} else {
cmdr.Info.Println("The system mounts have been performed successfully.")
}
return nil
}
{
cmdr.FgDefault.Println("mounting " + varPart.Device + " in /var")
if varPart.Device == "" {
return &PartNotFoundError{settings.Cnf.PartLabelVar}
}
if !dryRun {
err := varPart.Mount("/var")
if err != nil {
return err
}
}
return nil
}
{
type bindMount struct {
from, to string
options uintptr
}
binds := []bindMount{
{"/.system/usr", "/.system/usr", syscall.MS_RDONLY},
}
for _, bind := range binds {
cmdr.FgDefault.Println("bind-mounting " + bind.from + " to " + bind.to)
if !dryRun {
err := syscall.Mount(bind.from, bind.to, "", syscall.MS_BIND|bind.options, "")
if err != nil {
return err
}
}
}
return nil
}
{
type overlayMount struct {
destination string
lowerdirs []string
upperdir, workdir string
}
overlays := []overlayMount{
{"/.system/etc", []string{"/.system/etc"}, "/var/lib/abroot/etc/" + rootLabel, "/var/lib/abroot/etc/" + rootLabel + "-work"},
{"/opt", []string{"/.system/opt"}, "/var/opt", "/var/opt-work"},
}
for _, overlay := range overlays {
if _, err := os.Lstat(overlay.workdir); os.IsNotExist(err) {
err := os.MkdirAll(overlay.workdir, 0o755)
cmdr.Warning.Println(err)
// failing the boot here won't help so ingore any error
}
lowerCombined := strings.Join(overlay.lowerdirs, ":")
options := "lowerdir=" + lowerCombined + ",upperdir=" + overlay.upperdir + ",workdir=" + overlay.workdir
cmdr.FgDefault.Println("mounting overlay mount " + overlay.destination + " with options " + options)
if !dryRun {
err := syscall.Mount("overlay", overlay.destination, "overlay", 0, options)
if err != nil {
return err
}
}
}
return nil
}
{
cmdr.FgDefault.Println("switching the root in fstab")
const fstabFile = "/etc/fstab"
systemMounts := []string{"/", "/var", "/.system/usr", "/.system/etc"}
varBindMountExists := map[string]bool{
"/home": false,
"/media": false,
"/mnt": false,
"/root": false,
}
fstabContentsRaw, err := os.ReadFile(fstabFile)
if err != nil {
return err
}
fstabContents := string(fstabContentsRaw)
lines := strings.Split(fstabContents, "\n")
linesNew := make([]string, 0, len(lines))
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") {
linesNew = append(linesNew, line)
continue
}
words := strings.Fields(line)
if len(words) < 2 {
linesNew = append(linesNew, line)
continue
}
mountpoint := words[1]
if _, ok := varBindMountExists[mountpoint]; ok {
varBindMountExists[mountpoint] = true
linesNew = append(linesNew, line)
continue
}
// mounting to /var/home for example worked in the past
// this migrates those lines to use /home instead
if mountpointWithoutVar, found := strings.CutPrefix(mountpoint, "/var"); found {
if _, ok := varBindMountExists[mountpointWithoutVar]; ok {
cmdr.FgDefault.Println("Removing /var prefix from mount", mountpoint)
varBindMountExists[mountpointWithoutVar] = true
words[1] = mountpointWithoutVar
lineNew := strings.Join(words, " ")
linesNew = append(linesNew, lineNew)
continue
}
}
if slices.Contains(systemMounts, mountpoint) {
cmdr.FgDefault.Println("Deleting line: ", line)
continue
}
linesNew = append(linesNew, line)
}
for varBindMount, existsAlready := range varBindMountExists {
if existsAlready {
continue
}
newVarBindMountLine := fmt.Sprintf("/var%s %s none defaults,bind 0 0", varBindMount, varBindMount)
cmdr.FgDefault.Println("Adding line: ", newVarBindMountLine)
linesNew = append([]string{newVarBindMountLine}, linesNew...)
}
currentRootLine := "UUID=" + uuid + " / btrfs defaults 0 0"
cmdr.FgDefault.Println("Adding line: ", currentRootLine)
linesNew = append([]string{currentRootLine}, linesNew...)
newFstabContents := strings.Join(linesNew, "\n")
newFstabFile := fstabFile + ".new"
if !dryRun {
cmdr.FgDefault.Println("writing new fstab file")
err := os.WriteFile(newFstabFile, []byte(newFstabContents), 0o644)
if err != nil {
return err
}
err = core.AtomicSwap(fstabFile, newFstabFile)
if err != nil {
return err
}
err = os.Rename(newFstabFile, fstabFile+".old")
if err != nil {
cmdr.Warning.Println("Old Fstab file will keep .new suffix")
// ignore, backup is not neccessary to boot
}
}
return nil
}
import "embed"
import "github.com/vanilla-os/orchid/cmdr"
import "archive/tar"
import "compress/gzip"
import "encoding/json"
import "fmt"
import "io"
import "os"
import "path/filepath"
import "strings"
import "github.com/spf13/cobra"
import "github.com/google/uuid"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/abroot/settings"
import "github.com/vanilla-os/orchid/cmdr"
import "os"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/orchid/cmdr"
import "encoding/json"
import "fmt"
import "os"
import "strings"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/differ/diff"
import "github.com/vanilla-os/orchid/cmdr"
import "os"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/orchid/cmdr"
import "os"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/orchid/cmdr"
import "bufio"
import "errors"
import "os"
import "strings"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/orchid/cmdr"
import "errors"
import "fmt"
import "strings"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/orchid/cmdr"
import "errors"
import "os"
import "os/exec"
import "path/filepath"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/abroot/settings"
import "github.com/vanilla-os/orchid/cmdr"
import "errors"
import "os"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/orchid/cmdr"
import "fmt"
import "os"
import "slices"
import "strings"
import "syscall"
import "github.com/spf13/cobra"
import "github.com/vanilla-os/abroot/core"
import "github.com/vanilla-os/abroot/settings"
import "github.com/vanilla-os/orchid/cmdr"