2024-09-02 18:43:49 +00:00
|
|
|
/*
|
|
|
|
Jilo Agent
|
|
|
|
|
|
|
|
Description: Remote agent for Jilo Web with http API
|
|
|
|
Author: Yasen Pramatarov
|
|
|
|
License: GPLv2
|
|
|
|
Project URL: https://lindeas.com/jilo
|
|
|
|
Year: 2024
|
|
|
|
Version: 0.1
|
|
|
|
*/
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2024-09-02 19:28:45 +00:00
|
|
|
"io/ioutil"
|
2024-09-02 18:43:49 +00:00
|
|
|
"log"
|
|
|
|
"net/http"
|
2024-09-02 19:28:45 +00:00
|
|
|
"os"
|
2024-09-02 18:43:49 +00:00
|
|
|
"os/exec"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2024-09-02 19:28:45 +00:00
|
|
|
// Config holds the structure of the configuration file
|
|
|
|
type Config struct {
|
|
|
|
AgentPort int `json:"agent_port"`
|
|
|
|
NginxPort int `json:"nginx_port"`
|
2024-09-02 21:04:13 +00:00
|
|
|
ProsodyPort int `json:"prosody_port"`
|
2024-09-02 20:01:15 +00:00
|
|
|
JicofoStatsURL string `json:"jicofo_stats_url"`
|
2024-09-02 21:04:13 +00:00
|
|
|
JVBStatsURL string `json:"jvb_stats_url"`
|
2024-09-02 19:28:45 +00:00
|
|
|
}
|
|
|
|
|
2024-09-02 18:43:49 +00:00
|
|
|
// NginxData holds the nginx data structure for the API response to /nginx
|
|
|
|
type NginxData struct {
|
2024-09-02 19:28:45 +00:00
|
|
|
NginxState string `json:"nginx_state"`
|
2024-09-02 18:43:49 +00:00
|
|
|
NginxConnections int `json:"nginx_connections"`
|
|
|
|
}
|
|
|
|
|
2024-09-02 21:04:13 +00:00
|
|
|
// ProsodyData holds the prosody data structure for the API response to /prosody
|
|
|
|
type ProsodyData struct {
|
|
|
|
ProsodyState string `json:"prosody_state"`
|
|
|
|
ProsodyConnections int `json:"prosody_connections"`
|
|
|
|
}
|
|
|
|
|
2024-09-02 19:28:45 +00:00
|
|
|
// JicofoData holds the Jicofo data structure for the API response to /jicofo
|
|
|
|
type JicofoData struct {
|
|
|
|
JicofoState string `json:"jicofo_state"`
|
|
|
|
JicofoAPIData map[string]interface{} `json:"jicofo_api_data"`
|
|
|
|
}
|
|
|
|
|
2024-09-02 21:04:13 +00:00
|
|
|
// JVBData holds the JVB data structure for the API response to /jvb
|
|
|
|
type JVBData struct {
|
|
|
|
JVBState string `json:"jvb_state"`
|
|
|
|
JVBAPIData map[string]interface{} `json:"jvb_api_data"`
|
|
|
|
}
|
|
|
|
|
2024-09-02 20:01:15 +00:00
|
|
|
// getServiceState checks the status of the speciied service
|
|
|
|
func getServiceState(service string) string {
|
|
|
|
output, err := exec.Command("systemctl", "is-active", service).Output()
|
2024-09-02 18:43:49 +00:00
|
|
|
if err != nil {
|
2024-09-02 20:01:15 +00:00
|
|
|
log.Printf("Error checking the service \"%v\" state: %v", service, err)
|
2024-09-02 19:34:38 +00:00
|
|
|
return "error"
|
|
|
|
}
|
|
|
|
state := strings.TrimSpace(string(output))
|
|
|
|
if state == "active" {
|
|
|
|
return "running"
|
|
|
|
}
|
|
|
|
return "not running"
|
|
|
|
}
|
|
|
|
|
2024-09-02 21:04:13 +00:00
|
|
|
// getServiceConnections gets the number of active connections to the specified port
|
|
|
|
func getServiceConnections(service string, port int) int {
|
2024-09-02 20:01:15 +00:00
|
|
|
cmd := fmt.Sprintf("netstat -an | grep ':%d' | wc -l", port)
|
2024-09-02 19:28:45 +00:00
|
|
|
output, err := exec.Command("bash", "-c", cmd).Output()
|
2024-09-02 18:43:49 +00:00
|
|
|
if err != nil {
|
2024-09-02 21:04:13 +00:00
|
|
|
log.Printf("Error counting the \"%v\" connections: %v", service, err)
|
2024-09-02 18:43:49 +00:00
|
|
|
return -1
|
|
|
|
}
|
|
|
|
connections := strings.TrimSpace(string(output))
|
|
|
|
connectionsInt, err := strconv.Atoi(connections)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error converting connections to integer number: %v", err)
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
return connectionsInt
|
|
|
|
}
|
|
|
|
|
2024-09-02 20:01:15 +00:00
|
|
|
// getJitsiAPIData gets the response from the specified Jitsi stats API
|
|
|
|
func getJitsiAPIData(service string, url string) map[string]interface{} {
|
|
|
|
cmd := fmt.Sprintf("curl -s %v", url)
|
|
|
|
output, err := exec.Command("bash", "-c", cmd).Output()
|
2024-09-02 19:28:45 +00:00
|
|
|
if err != nil {
|
2024-09-02 20:01:15 +00:00
|
|
|
log.Printf("Error getting the \"%v\" API stats: %v", service, err)
|
|
|
|
return map[string]interface{}{"error": "failed to get the Jitsi API stats"}
|
2024-09-02 19:28:45 +00:00
|
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
|
|
if err := json.Unmarshal(output, &result); err != nil {
|
|
|
|
log.Printf("Error in parsing the JSON: %v", err)
|
|
|
|
return map[string]interface{}{"error": "invalid JSON format"}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// loadConfig loads the configuration from a JSON config file
|
2024-09-02 21:04:13 +00:00
|
|
|
func loadConfig(filename string) (Config) {
|
|
|
|
|
|
|
|
// default config values
|
|
|
|
config := Config {
|
|
|
|
AgentPort: 8081, // default Agent port (we avoid 80, 443, 8080 and 8888)
|
|
|
|
NginxPort: 80, // default nginx port
|
|
|
|
ProsodyPort: 5222, // default prosody port
|
|
|
|
JicofoStatsURL: "http://localhost:8888/stats", // default Jicofo stats URL
|
|
|
|
JVBStatsURL: "http://localhost:8080/colibri/stats", // default JVB stats URL
|
|
|
|
}
|
2024-09-02 19:28:45 +00:00
|
|
|
|
2024-09-02 21:04:13 +00:00
|
|
|
// we try to load the config file; use default values otherwise
|
2024-09-02 19:28:45 +00:00
|
|
|
file, err := os.Open(filename)
|
|
|
|
if err != nil {
|
2024-09-02 21:04:13 +00:00
|
|
|
log.Printf("Can't open the config file \"%v\". Using default values.", filename)
|
|
|
|
return config
|
2024-09-02 19:28:45 +00:00
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
bytes, err := ioutil.ReadAll(file)
|
|
|
|
if err != nil {
|
2024-09-02 21:04:13 +00:00
|
|
|
log.Printf("There was an error reading the config file. Using default values")
|
|
|
|
return config
|
2024-09-02 19:28:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(bytes, &config); err != nil {
|
2024-09-02 21:04:13 +00:00
|
|
|
log.Printf("Error parsing the config file. Using default values.")
|
2024-09-02 19:28:45 +00:00
|
|
|
}
|
|
|
|
|
2024-09-02 21:04:13 +00:00
|
|
|
return config
|
2024-09-02 19:28:45 +00:00
|
|
|
}
|
|
|
|
|
2024-09-02 18:43:49 +00:00
|
|
|
// nginxHandler handles the /nginx endpoint
|
2024-09-02 19:28:45 +00:00
|
|
|
func nginxHandler(config Config, w http.ResponseWriter, r *http.Request) {
|
2024-09-02 18:43:49 +00:00
|
|
|
data := NginxData {
|
2024-09-02 20:01:15 +00:00
|
|
|
NginxState: getServiceState("nginx"),
|
2024-09-02 21:04:13 +00:00
|
|
|
NginxConnections: getServiceConnections("nginx", config.NginxPort),
|
|
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// prosodyHandler handles the /prosody endpoint
|
|
|
|
func prosodyHandler(config Config, w http.ResponseWriter, r *http.Request) {
|
|
|
|
data := ProsodyData {
|
|
|
|
ProsodyState: getServiceState("prosody"),
|
|
|
|
ProsodyConnections: getServiceConnections("prosody", config.ProsodyPort),
|
2024-09-02 18:43:49 +00:00
|
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(data)
|
|
|
|
}
|
|
|
|
|
2024-09-02 19:34:38 +00:00
|
|
|
// jicofoHandler handles the /jicofo endpoint
|
|
|
|
func jicofoHandler(config Config, w http.ResponseWriter, r *http.Request) {
|
|
|
|
data := JicofoData {
|
2024-09-02 20:01:15 +00:00
|
|
|
JicofoState: getServiceState("jicofo"),
|
|
|
|
JicofoAPIData: getJitsiAPIData("jicofo", config.JicofoStatsURL),
|
2024-09-02 19:34:38 +00:00
|
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(data)
|
|
|
|
}
|
|
|
|
|
2024-09-02 21:04:13 +00:00
|
|
|
// jvbHandler handles the /jvb endpoint
|
|
|
|
func jvbHandler(config Config, w http.ResponseWriter, r *http.Request) {
|
|
|
|
data := JVBData {
|
|
|
|
JVBState: getServiceState("jitsi-videobridge2"),
|
|
|
|
JVBAPIData: getJitsiAPIData("jitsi-videobridge2", config.JVBStatsURL),
|
|
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(data)
|
|
|
|
}
|
|
|
|
|
2024-09-02 19:34:38 +00:00
|
|
|
|
2024-09-02 18:43:49 +00:00
|
|
|
// main sets up the http server and the routes
|
|
|
|
func main() {
|
2024-09-02 19:28:45 +00:00
|
|
|
|
|
|
|
// load the configuration
|
2024-09-02 21:04:13 +00:00
|
|
|
config := loadConfig("jilo-agent.json")
|
2024-09-02 19:28:45 +00:00
|
|
|
|
|
|
|
// endpoints
|
|
|
|
http.HandleFunc("/nginx", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
nginxHandler(config, w, r)
|
|
|
|
})
|
2024-09-02 21:04:13 +00:00
|
|
|
http.HandleFunc("/prosody", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
prosodyHandler(config, w, r)
|
|
|
|
})
|
2024-09-02 19:34:38 +00:00
|
|
|
http.HandleFunc("/jicofo", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
jicofoHandler(config, w, r)
|
|
|
|
})
|
2024-09-02 21:04:13 +00:00
|
|
|
http.HandleFunc("/jvb", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
jvbHandler(config, w, r)
|
|
|
|
})
|
2024-09-02 19:28:45 +00:00
|
|
|
|
|
|
|
// start the http server
|
|
|
|
agentPortStr := fmt.Sprintf(":%d", config.AgentPort)
|
2024-09-02 19:34:38 +00:00
|
|
|
fmt.Printf("Starting Jilo agent server on port %d.\n", config.AgentPort)
|
2024-09-02 19:28:45 +00:00
|
|
|
if err := http.ListenAndServe(agentPortStr, nil); err != nil {
|
2024-09-02 18:43:49 +00:00
|
|
|
log.Fatalf("Could not start the server: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|