feat: add reboot and shutdown

This commit is contained in:
2025-05-13 14:37:32 +02:00
parent 9c80c1e771
commit ee0489dcbb
7 changed files with 144 additions and 21 deletions

View File

@@ -7,6 +7,7 @@ import (
"net/http"
network "github.com/0x1d/rcond/pkg/network"
"github.com/0x1d/rcond/pkg/system"
"github.com/0x1d/rcond/pkg/user"
"github.com/gorilla/mux"
)
@@ -256,3 +257,33 @@ func HandleRemoveAuthorizedKey(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "success"})
}
func HandleReboot(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if err := system.Restart(); err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "success"})
}
func HandleShutdown(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if err := system.Shutdown(); err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "success"})
}

View File

@@ -67,6 +67,8 @@ func (s *Server) RegisterRoutes() {
s.router.HandleFunc("/hostname", s.verifyToken(HandleSetHostname)).Methods(http.MethodPost)
s.router.HandleFunc("/users/{user}/keys", s.verifyToken(HandleAddAuthorizedKey)).Methods(http.MethodPost)
s.router.HandleFunc("/users/{user}/keys/{fingerprint}", s.verifyToken(HandleRemoveAuthorizedKey)).Methods(http.MethodDelete)
s.router.HandleFunc("/system/restart", s.verifyToken(HandleReboot)).Methods(http.MethodPost)
s.router.HandleFunc("/system/shutdown", s.verifyToken(HandleShutdown)).Methods(http.MethodPost)
}
func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {

View File

@@ -6,6 +6,7 @@ import (
"os"
"time"
"github.com/0x1d/rcond/pkg/system"
"github.com/godbus/dbus/v5"
"github.com/google/uuid"
)
@@ -64,21 +65,6 @@ func DefaultAPConfig(uuid uuid.UUID, ssid string, password string, autoconnect b
}
}
// withDbus executes the given function with a D-Bus system connection
// and handles any connection errors
func withDbus(fn func(*dbus.Conn) error) error {
conn, err := dbus.SystemBus()
if err != nil {
log.Printf("Failed to connect to system bus: %v", err)
return err
}
if err := fn(conn); err != nil {
log.Print(err)
return err
}
return nil
}
// ActivateConnection activates a NetworkManager connection profile.
// It takes a D-Bus connection, connection profile path, and device path as arguments.
// The function waits up to 10 seconds for the connection to become active.
@@ -259,6 +245,7 @@ func AddConnectionWithConfig(conn *dbus.Conn, cfg *ConnectionConfig) (dbus.Objec
var wirelessMap map[string]dbus.Variant
if cfg.Mode == "ap" {
wirelessMap = map[string]dbus.Variant{
"ssid": dbus.MakeVariant([]byte(cfg.SSID)),
"mode": dbus.MakeVariant(cfg.Mode),
"band": dbus.MakeVariant(cfg.Band),
"channel": dbus.MakeVariant(cfg.Channel),
@@ -266,6 +253,7 @@ func AddConnectionWithConfig(conn *dbus.Conn, cfg *ConnectionConfig) (dbus.Objec
} else {
wirelessMap = map[string]dbus.Variant{
"ssid": dbus.MakeVariant([]byte(cfg.SSID)),
"mode": dbus.MakeVariant(cfg.Mode),
}
}
@@ -335,7 +323,7 @@ func GetHostname() (string, error) {
// SetHostname changes the static hostname via the system bus.
// newHost is your desired hostname, interactive=false skips any prompt.
func SetHostname(newHost string) error {
return withDbus(func(conn *dbus.Conn) error {
return system.WithDbus(func(conn *dbus.Conn) error {
obj := conn.Object(
"org.freedesktop.hostname1",
dbus.ObjectPath("/org/freedesktop/hostname1"),
@@ -356,7 +344,7 @@ func SetHostname(newHost string) error {
func ConfigureSTA(iface string, ssid string, password string, autoconnect bool) (string, error) {
uuid := uuid.New()
err := withDbus(func(conn *dbus.Conn) error {
err := system.WithDbus(func(conn *dbus.Conn) error {
_, err := AddStationConnection(conn, uuid, ssid, password, autoconnect)
if err != nil {
return fmt.Errorf("failed to create station connection: %v", err)
@@ -378,7 +366,7 @@ func ConfigureSTA(iface string, ssid string, password string, autoconnect bool)
func ConfigureAP(iface string, ssid string, password string, autoconnect bool) (string, error) {
uuid := uuid.New()
err := withDbus(func(conn *dbus.Conn) error {
err := system.WithDbus(func(conn *dbus.Conn) error {
_, err := AddAccessPointConnection(conn, uuid, ssid, password, autoconnect)
if err != nil {
return fmt.Errorf("failed to create access point connection: %v", err)
@@ -399,7 +387,7 @@ func ConfigureAP(iface string, ssid string, password string, autoconnect bool) (
// The connection will be activated on the specified interface.
// Returns an error if any operation fails.
func Up(iface string, uuid string) error {
return withDbus(func(conn *dbus.Conn) error {
return system.WithDbus(func(conn *dbus.Conn) error {
connPath, err := GetConnectionPath(conn, uuid)
if err != nil {
return err
@@ -429,7 +417,7 @@ func Up(iface string, uuid string) error {
// It takes the interface name as an argument.
// Returns an error if the device cannot be found or disconnected.
func Down(iface string) error {
return withDbus(func(conn *dbus.Conn) error {
return system.WithDbus(func(conn *dbus.Conn) error {
devPath, err := GetDeviceByIpIface(conn, iface)
if err != nil {
return err
@@ -447,7 +435,7 @@ func Down(iface string) error {
// If no connection with the UUID exists, it returns nil.
// Returns an error if the connection exists but cannot be deleted.
func Remove(uuid string) error {
return withDbus(func(conn *dbus.Conn) error {
return system.WithDbus(func(conn *dbus.Conn) error {
connPath, err := GetConnectionPath(conn, uuid)
if err != nil {
return err

23
pkg/system/dbus.go Normal file
View File

@@ -0,0 +1,23 @@
package system
import (
"log"
"github.com/godbus/dbus/v5"
)
// WithDbus executes the given function with a D-Bus system connection
// and handles any connection errors
func WithDbus(fn func(*dbus.Conn) error) error {
conn, err := dbus.SystemBus()
if err != nil {
log.Printf("Failed to connect to system bus: %v", err)
return err
}
if err := fn(conn); err != nil {
log.Print(err)
return err
}
conn.Close()
return nil
}

33
pkg/system/state.go Normal file
View File

@@ -0,0 +1,33 @@
package system
import (
"log"
"github.com/godbus/dbus/v5"
)
// Restart restarts the system.
func Restart() error {
return WithDbus(func(conn *dbus.Conn) error {
obj := conn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1")
log.Println("Rebooting system...")
call := obj.Call("org.freedesktop.systemd1.Manager.Reboot", 0)
if call.Err != nil {
log.Fatal(call.Err)
}
return nil
})
}
// Shutdown shuts down the system.
func Shutdown() error {
return WithDbus(func(conn *dbus.Conn) error {
obj := conn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1")
log.Println("Shutting down system...")
call := obj.Call("org.freedesktop.systemd1.Manager.PowerOff", 0)
if call.Err != nil {
log.Fatal(call.Err)
}
return nil
})
}