github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/cmd/cli/commands/server/server.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  // The following commands will run pingmq as a server, pinging the 8.8.8.0/28 CIDR
    20  // block, and publishing the results to /ping/success/{ip} and /ping/failure/{ip}
    21  // topics every 30 seconds. `sudo` is needed because we are using RAW sockets and
    22  // that requires root privilege.
    23  //
    24  //	$ go build
    25  //	$ sudo ./pingmq server -p 8.8.8.0/28 -i 30
    26  package server
    27  
    28  import (
    29  	"fmt"
    30  	"log"
    31  	"net"
    32  	"strings"
    33  	"time"
    34  
    35  	"github.com/DrmagicE/gmqtt"
    36  	"github.com/DrmagicE/gmqtt/server"
    37  	"github.com/e154/smart-home/system/mqtt"
    38  	"github.com/koron/netx"
    39  	"github.com/spf13/cobra"
    40  )
    41  
    42  var (
    43  	// Server ...
    44  	Server = &cobra.Command{
    45  		Use:   "server",
    46  		Short: "server starts a SurgeMQ server and publishes to it all the ping results",
    47  	}
    48  
    49  	serverURI    string
    50  	serverQuiet  bool
    51  	serverIPs    strlist
    52  	pingInterval int
    53  
    54  	s mqtt.GMqttServer
    55  
    56  	p *netx.Pinger
    57  )
    58  
    59  type strlist []string
    60  
    61  // String ...
    62  func (s *strlist) String() string {
    63  	return fmt.Sprint(*s)
    64  }
    65  
    66  // Type ...
    67  func (s *strlist) Type() string {
    68  	return "strlist"
    69  }
    70  
    71  // Set ...
    72  func (s *strlist) Set(value string) error {
    73  	for _, ip := range strings.Split(value, ",") {
    74  		*s = append(*s, ip)
    75  	}
    76  
    77  	return nil
    78  }
    79  
    80  func init() {
    81  	Server.Flags().StringVarP(&serverURI, "uri", "u", "0.0.0.0:1883", "URI to run the server on")
    82  	Server.Flags().BoolVarP(&serverQuiet, "quiet", "q", false, "print out ping results")
    83  	Server.Flags().VarP(&serverIPs, "ping", "p", "Comma separated list of IPv4 addresses to ping")
    84  	Server.Flags().IntVarP(&pingInterval, "interval", "i", 60, "ping interval in seconds")
    85  	Server.Run = serv
    86  
    87  }
    88  
    89  func serv(cmd *cobra.Command, args []string) {
    90  
    91  	log.Printf("Starting server...")
    92  	go func() {
    93  		ln, err := net.Listen("tcp", serverURI)
    94  		if err != nil {
    95  			log.Fatal(err.Error())
    96  			return
    97  		}
    98  
    99  		options := []server.Options{
   100  			server.WithTCPListener(ln),
   101  		}
   102  
   103  		// Create a new server
   104  		s = server.New(options...)
   105  
   106  		if err = s.Run(); err != nil {
   107  			log.Println(err.Error())
   108  		}
   109  
   110  	}()
   111  	time.Sleep(300 * time.Millisecond)
   112  
   113  	log.Printf("Starting pinger...")
   114  
   115  	pinger()
   116  }
   117  
   118  func pinger() {
   119  
   120  	p = &netx.Pinger{}
   121  	if err := p.AddIPs(serverIPs); err != nil {
   122  		log.Fatal(err)
   123  	}
   124  
   125  	cnt := 0
   126  	tick := time.NewTicker(time.Duration(pingInterval) * time.Second)
   127  
   128  	for {
   129  		if cnt != 0 {
   130  			<-tick.C
   131  		}
   132  
   133  		res, err := p.Start()
   134  		if err != nil {
   135  			log.Fatal(err)
   136  		}
   137  
   138  		for pr := range res {
   139  			if !serverQuiet {
   140  				log.Println(pr)
   141  			}
   142  
   143  			var topic string
   144  
   145  			// Creates a new PUBLISH message with the appropriate contents for publishing
   146  			if pr.Err != nil {
   147  				topic = fmt.Sprintf("/ping/failure/%s", pr.Src)
   148  			} else {
   149  				topic = fmt.Sprintf("/ping/success/%s", pr.Src)
   150  			}
   151  
   152  			payload, err := pr.GobEncode()
   153  			if err != nil {
   154  				log.Printf("pinger: Error from GobEncode: %v\n", err)
   155  				continue
   156  			}
   157  
   158  			// Publishes to the server
   159  			s.Publisher().Publish(&gmqtt.Message{
   160  				Topic:    topic,
   161  				Payload:  payload,
   162  				QoS:      0,
   163  				Retained: true,
   164  			})
   165  		}
   166  
   167  		p.Stop()
   168  		cnt++
   169  	}
   170  }