Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gerblesh committed Nov 28, 2024
1 parent ae4e70a commit d209efa
Show file tree
Hide file tree
Showing 15 changed files with 618 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ go.work.sum

# env file
.env
ublue-upd
185 changes: 185 additions & 0 deletions checks/hardware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package checks

import (
"fmt"

"github.com/godbus/dbus/v5"
"github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/mem"
)

type Info struct {
Name string
Err error
}

func Hardware(conn *dbus.Conn) []Info {
var checks []Info
checks = append(checks, battery(conn))
checks = append(checks, network(conn))
checks = append(checks, cpu())
checks = append(checks, memory())

return checks
}

func battery(conn *dbus.Conn) Info {
const name string = "Battery"
upower := conn.Object("org.freedesktop.UPower", "/org/freedesktop/UPower")
// first, check if the device is running on battery
variant, err := upower.GetProperty("org.freedesktop.UPower.OnBattery")
if err != nil {
return Info{
name,
err,
}
}

onBattery, ok := variant.Value().(bool)
if !ok {
return Info{
name,
fmt.Errorf("Unable to determine if this computer is running on battery with: %v", variant),
}
}
// Not running on battery, skip this test
if !onBattery {
return Info{
name,
nil,
}
}

dev := conn.Object("org.freedesktop.UPower", "/org/freedesktop/UPower/devices/DisplayDevice")
variant, err = dev.GetProperty("org.freedesktop.UPower.Device.Percentage")
if err != nil {
return Info{
name,
err,
}
}

batteryPercent, ok := variant.Value().(float64)

if !ok {
return Info{
name,
fmt.Errorf("Unable to get battery percent from: %v", variant),
}
}
if batteryPercent < 20 {
return Info{
name,
fmt.Errorf("Battery percent below 20, detected battery percent: %v", batteryPercent),
}
}

return Info{
name,
nil,
}
}

func network(conn *dbus.Conn) Info {
const name string = "Network"

nm := conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")

variant, err := nm.GetProperty("org.freedesktop.NetworkManager.Metered")
if err != nil {
return Info{
name,
err,
}
}
metered, ok := variant.Value().(uint32)
if !ok {
return Info{
name,
fmt.Errorf("Unable to determine if network connection is metered from: %v", variant),
}
}
// The possible values of "Metered" are documented here:
// https://networkmanager.dev/docs/api/latest/nm-dbus-types.html//NMMetered
//
// NM_METERED_UNKNOWN = 0 // The metered status is unknown
// NM_METERED_YES = 1 // Metered, the value was explicitly configured
// NM_METERED_NO = 2 // Not metered, the value was explicitly configured
// NM_METERED_GUESS_YES = 3 // Metered, the value was guessed
// NM_METERED_GUESS_NO = 4 // Not metered, the value was guessed
//
if metered == 1 || metered == 3 {
return Info{
name,
fmt.Errorf("Network is metered"),
}
}

// check if user is connected to network
var connectivity uint32
err = nm.Call("org.freedesktop.NetworkManager.CheckConnectivity", 0).Store(&connectivity)
if err != nil {
return Info{
name,
err,
}
}

// 4 means fully connected: https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMConnectivityState
if connectivity != 4 {
return Info{
name,
fmt.Errorf("Network not online"),
}
}

return Info{
name,
nil,
}

}

func memory() Info {
const name string = "Memory"
v, err := mem.VirtualMemory()
if err != nil {
return Info{
name,
err,
}
}
if v.UsedPercent > 90.0 {
return Info{
name,
fmt.Errorf("Current memory usage above 90 percent: %v", v.UsedPercent),
}
}
return Info{
name,
nil,
}
}

func cpu() Info {
const name string = "CPU"
avg, err := load.Avg()
if err != nil {
return Info{
name,
err,
}
}
// Check if the CPU load in the 5 minutes was greater than 50%
if avg.Load5 > 50.0 {
return Info{
name,
fmt.Errorf("CPU load above 50 percent: %v", avg.Load5),
}
}

return Info{
name,
nil,
}
}
25 changes: 25 additions & 0 deletions cmd/hw-check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cmd

import (
"log"

"github.com/gerblesh/update-next/checks"
"github.com/godbus/dbus/v5"
"github.com/spf13/cobra"
)

func HwCheck(cmd *cobra.Command, args []string) {
// (some hardware checks require dbus access)
conn, err := dbus.SystemBus()
if err != nil {
log.Fatalf("Failed to connect to session bus: %v", err)
}
defer conn.Close()
checkInfo := checks.Hardware(conn)
for _, info := range checkInfo {
if info.Err != nil {
log.Fatalf("Hardware checks failed for %s, returned error: %v", info.Name, info.Err)
}
}
log.Println("Hardware checks passed")
}
47 changes: 47 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package cmd

import (
"fmt"
"github.com/spf13/cobra"
"os"
)

var (
rootCmd = &cobra.Command{
Use: "ublue-upd",
Short: "ublue-upd is the successor to ublue-update, built for bootc",
Run: Update,
}

waitCmd = &cobra.Command{
Use: "wait",
Short: "Wait for a condition or time period",
Long: "This command simulates waiting for some event or process to complete.",
Run: Wait,
}

updateCheckCmd = &cobra.Command{
Use: "update-check",
Short: "Check for updates to the booted image",
Run: UpdateCheck,
}

hardwareCheckCmd = &cobra.Command{
Use: "hw-check",
Short: "Run hardware checks",
Run: HwCheck,
}
)

func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

func init() {
rootCmd.AddCommand(waitCmd)
rootCmd.AddCommand(updateCheckCmd)
rootCmd.AddCommand(hardwareCheckCmd)
}
54 changes: 54 additions & 0 deletions cmd/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cmd

import (
"github.com/gerblesh/update-next/checks"
"github.com/gerblesh/update-next/drv"
"github.com/gerblesh/update-next/lib"
"github.com/godbus/dbus/v5"
// "github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"log"
)

func Update(cmd *cobra.Command, args []string) {

// (some hardware checks require dbus access)
conn, err := dbus.SystemBus()
if err != nil {
log.Fatalf("Failed to connect to session bus: %v", err)
}
defer conn.Close()
checkInfo := checks.Hardware(conn)
for _, info := range checkInfo {
if info.Err != nil {
log.Fatalf("Hardware checks failed for %s, returned error: %v", info.Name, info.Err)
}
}
log.Println("Hardware checks passed")

updateAvailable, err := drv.CheckForUpdate()
if err != nil {
log.Fatalf("Failed to check for image updates: %v", err)
}
log.Printf("update available: %v", updateAvailable)
if updateAvailable {
err = drv.BootcUpdate()
if err != nil {
log.Fatalf("Failed to update system: %v", err)
}
}
err = drv.BrewUpdate()
if err != nil {
log.Fatalf("Failed to update brew: %v", err)
}

users, err := lib.ListUsers()
if err != nil {
log.Fatalf("ERROR: %v", err)
}
err = drv.FlatpakUpdate(users)
if err != nil {
log.Fatalf("Failed to update flatpak: %v", err)
}

}
15 changes: 15 additions & 0 deletions cmd/updateCheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cmd

import (
"github.com/gerblesh/update-next/drv"
"github.com/spf13/cobra"
"log"
)

func UpdateCheck(cmd *cobra.Command, args []string) {
update, err := drv.CheckForUpdate()
if err != nil {
log.Fatalf("Failed to check for updates: %v", err)
}
log.Printf("Update Available: %v", update)
}
34 changes: 34 additions & 0 deletions cmd/wait.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cmd

import (
"log"
"os"
"time"

"github.com/gerblesh/update-next/lib"
"github.com/spf13/cobra"
)

func Wait(cmd *cobra.Command, args []string) {
// TODO: rely on bootc to do transaction wait
lockFilePath := "/sysroot/ostree/lock"

for {

time.Sleep(2 * time.Second)
file, err := os.Open(lockFilePath)
if err != nil {
// file must not exist
break
}

if lib.IsFileLocked(file) {
file.Close()
log.Printf("Waiting for lockfile: %s", lockFilePath)
} else {
file.Close()
break
}
}
log.Printf("Done Waiting!")
}
24 changes: 24 additions & 0 deletions drv/bootc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package drv

import (
"os/exec"
"strings"
)

func BootcUpdate() error {
cmd := exec.Command("/usr/bin/bootc", "upgrade")
err := cmd.Run()
if err != nil {
return err
}
return nil
}

func CheckForUpdate() (bool, error) {
cmd := exec.Command("/usr/bin/bootc", "upgrade", "--check")
out, err := cmd.Output()
if err != nil {
return true, err
}
return !strings.Contains(string(out), "No changes in:"), nil
}
Loading

0 comments on commit d209efa

Please sign in to comment.