diff --git a/README.md b/README.md index 1802d2f..89e0a2b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ The full API specification can be found in [api/rcond.yaml](api/rcond.yaml). | POST | `/network/up` | Create and activate a WiFi access point | | POST | `/network/down` | Deactivate a WiFi interface | | POST | `/network/remove` | Remove the stored connection profile | +| GET | `/hostname` | Get the hostname | +| POST | `/hostname` | Set the hostname | ### Response Codes @@ -39,7 +41,7 @@ The full API specification can be found in [api/rcond.yaml](api/rcond.yaml). ### Request/Response Format All endpoints use JSON for request and response payloads. -### 1) Bring a network up +### Bring a network up ```bash curl -v -X POST http://localhost:8080/network/up \ @@ -51,7 +53,7 @@ curl -v -X POST http://localhost:8080/network/up \ }' ``` -### 2) Bring a network down +### Bring a network down ```bash curl -v -X POST http://localhost:8080/network/down \ @@ -61,8 +63,24 @@ curl -v -X POST http://localhost:8080/network/down \ }' ``` -### 3) Remove the stored connection +### Remove the stored connection ```bash curl -v -X POST http://localhost:8080/network/remove ``` + +### Get the hostname + +```bash +curl -v http://localhost:8080/hostname +``` + +### Set the hostname + +```bash +curl -v -X POST http://localhost:8080/hostname \ + -H "Content-Type: application/json" \ + -d '{ + "hostname": "MyHostname" + }' +``` \ No newline at end of file diff --git a/api/rcond.yaml b/api/rcond.yaml index bbc565f..c7ad5cd 100644 --- a/api/rcond.yaml +++ b/api/rcond.yaml @@ -94,3 +94,43 @@ paths: description: Connection profile removed successfully '500': description: Internal server error + + /hostname: + get: + summary: Get system hostname + description: Returns the current system hostname + responses: + '200': + description: Hostname retrieved successfully + content: + text/plain: + schema: + type: string + description: Current hostname + example: "MyHostname" + '500': + description: Internal server error + post: + summary: Set system hostname + description: Sets a new system hostname + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - hostname + properties: + hostname: + type: string + description: New hostname to set + example: "MyHostname" + responses: + '200': + description: Hostname set successfully + '400': + description: Invalid request payload + '500': + description: Internal server error + diff --git a/pkg/http/handlers.go b/pkg/http/handlers.go index 60c5417..a980f39 100644 --- a/pkg/http/handlers.go +++ b/pkg/http/handlers.go @@ -76,3 +76,35 @@ func HandleNetworkRemove(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } + +func HandleGetHostname(w http.ResponseWriter, r *http.Request) { + hostname, err := network.GetHostname() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write([]byte(hostname)) +} + +func HandleSetHostname(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var req struct { + Hostname string `json:"hostname"` + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + if err := network.SetHostname(req.Hostname); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} diff --git a/pkg/http/server.go b/pkg/http/server.go index 5a9f969..20cf0bc 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -43,6 +43,8 @@ func (s *Server) RegisterRoutes() { s.router.HandleFunc("/network/up", HandleNetworkUp).Methods(http.MethodPost) s.router.HandleFunc("/network/down", HandleNetworkDown).Methods(http.MethodPost) s.router.HandleFunc("/network/remove", HandleNetworkRemove).Methods(http.MethodPost) + s.router.HandleFunc("/hostname", HandleGetHostname).Methods(http.MethodGet) + s.router.HandleFunc("/hostname", HandleSetHostname).Methods(http.MethodPost) } func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/network/nm.go b/pkg/network/nm.go index 8f8d8d0..95d6e5e 100644 --- a/pkg/network/nm.go +++ b/pkg/network/nm.go @@ -253,6 +253,32 @@ func GetDeviceByIpIface(conn *dbus.Conn, iface string) (dbus.ObjectPath, error) return devPath, nil } +// GetHostname returns the hostname of the current machine +func GetHostname() (string, error) { + hostname, err := os.Hostname() + if err != nil { + return "", fmt.Errorf("GetHostname failed: %v", err) + } + return hostname, nil +} + +// 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 { + obj := conn.Object( + "org.freedesktop.hostname1", + dbus.ObjectPath("/org/freedesktop/hostname1"), + ) + return obj.Call( + "org.freedesktop.hostname1.SetStaticHostname", + 0, // no special flags + newHost, // the hostname you want + false, // interactive? (PolicyKit) + ).Err + }) +} + // Up creates and activates a WiFi access point connection. // It takes the interface name, SSID, password and UUID as arguments. // If a connection with the given UUID exists, it will be reused.