package main import ( "strconv" "bytes" "log" "net" "os" "time" "os/exec" "github.com/eclipse/paho.mqtt.golang" "github.com/shirou/gopsutil/host" "github.com/google/uuid" "github.com/urfave/cli" ) //GetOutboundIP Gets the preferred outbound ip of this machine func GetOutboundIP() net.IP { conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil { log.Fatal(err) } defer conn.Close() localAddr := conn.LocalAddr().(*net.UDPAddr) return localAddr.IP } //getMacAddr fetches the first avaible Mac address func getMacAddr() (addr string) { interfaces, err := net.Interfaces() if err == nil { for _, i := range interfaces { if i.Flags&net.FlagUp != 0 && bytes.Compare(i.HardwareAddr, nil) != 0 { // Don't use random as we have a real address addr = i.HardwareAddr.String() break } } } return } //SyncPublish Synchronous publish an MQTT topic and wait for confirmation func SyncPublish(client mqtt.Client, topic string, qos byte, retained bool, payload interface{}) { hostname, _ := os.Hostname() prefix := "homie/" + hostname + "/" token := client.Publish(prefix + topic, qos, retained, payload) if(!token.Wait()) { log.Fatal("Timeout on publish") os.Exit(1) } } //cmdRun Command handler for the run command func cmdRun(cli *cli.Context) { clientid := uuid.New() command := ""; if cli.NArg() > 0 { command = cli.Args()[0] } else { log.Fatal("You must provide a command to run") os.Exit(1) } log.Printf("Connection string: %s\n",cli.GlobalString("mqtt")) opts := mqtt.NewClientOptions().AddBroker(cli.GlobalString("mqtt")).SetClientID(clientid.String()) hostname, _ := os.Hostname() opts.SetWill("homie/" + hostname + "/$state", "lost", 0, true); client := mqtt.NewClient(opts) log.Println("Connecting...") if token := client.Connect(); token.Wait() && token.Error() != nil { log.Fatal(token.Error()) os.Exit(1) } log.Println("Connected") SyncPublish(client, "$state", 0, true, "init") log.Println("$state -> init") SyncPublish(client, "$homie", 0, true, "3.0.1") SyncPublish(client, "$name", 0, true, hostname) SyncPublish(client, "$localip", 0, true, GetOutboundIP().String()) SyncPublish(client, "$mac", 0, true, getMacAddr()) SyncPublish(client, "$fw/name", 0, true, "gomie") SyncPublish(client, "$fw/version", 0, true, "1.0") SyncPublish(client, "$implementation", 0, true, "gomie") SyncPublish(client, "$stats", 0, true, "uptime") uptimeSeconds, _ := host.Uptime() SyncPublish(client, "$stats/uptime", 0, true, strconv.FormatUint(uptimeSeconds,10)) SyncPublish(client, "$stats/interval", 0, true, "0") SyncPublish(client, "leax-backup/$name", 0, true, "LEAX Backup") SyncPublish(client, "leax-backup/$type", 0, true, "script") SyncPublish(client, "leax-backup/$properties", 0, true, "lastrun,stdout,errorcode") SyncPublish(client, "$state", 0, true, "ready") log.Println("$state -> ready") //Done with initialization SyncPublish(client, "leax-backup/started", 0, true, strconv.FormatInt(time.Now().Unix(),10)) SyncPublish(client, "leax-backup/stdout", 0, true, "") out, err := exec.Command(command).Output() if err != nil { log.Fatal(err) } SyncPublish(client, "leax-backup/ended", 0, true, strconv.FormatInt(time.Now().Unix(),10)) SyncPublish(client, "leax-backup/stdout", 0, true, out) //All done SyncPublish(client, "$state", 0, true, "disconnected") log.Println("$state -> disconnected") client.Disconnect(1000) //Wait one second to disconnect log.Println("Disconnected") }