src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

wwan.go (2222B)


      1 package wwan
      2 
      3 import (
      4 	"bufio"
      5 	"bytes"
      6 	"context"
      7 	"fmt"
      8 	"net"
      9 	"os/exec"
     10 	"path"
     11 	"strings"
     12 )
     13 
     14 type Config struct {
     15 	Iface string `json:"iface"`
     16 }
     17 
     18 type Block struct {
     19 	iface string
     20 }
     21 
     22 func New(iface string) *Block {
     23 	return &Block{iface: iface}
     24 }
     25 
     26 func (b *Block) Name() string {
     27 	return "wwan"
     28 }
     29 
     30 // TODO: signal strength icon.
     31 // TODO: get IP address (net.Interfaces).
     32 func (b *Block) Render(ctx context.Context) (string, error) {
     33 	out, err := exec.CommandContext(ctx, "mmcli", "--list-modems").Output()
     34 	if err != nil {
     35 		return "", fmt.Errorf("exec mmcli failed: %v", err)
     36 	}
     37 
     38 	fields := strings.Fields(string(out))
     39 	if len(fields) == 0 {
     40 		return "", fmt.Errorf("unexpected output: %v", err)
     41 	}
     42 	modem := path.Base(fields[0])
     43 
     44 	out, err = exec.CommandContext(
     45 		ctx, "mmcli", "-m", modem, "--output-keyvalue",
     46 	).Output()
     47 	if err != nil {
     48 		return "", fmt.Errorf("exec mmcli failed: %v", err)
     49 	}
     50 
     51 	var (
     52 		scanner = bufio.NewScanner(bytes.NewReader(out))
     53 
     54 		state, signal string
     55 	)
     56 	for scanner.Scan() {
     57 		fields := strings.Fields(scanner.Text())
     58 		if len(fields) < 3 {
     59 			continue
     60 		}
     61 
     62 		switch {
     63 		case fields[0] == "modem.generic.state":
     64 			state = fields[2]
     65 		case fields[0] == "modem.generic.signal-quality.value":
     66 			signal = fields[2]
     67 		}
     68 	}
     69 
     70 	if state == "disabled" {
     71 		return " ", nil
     72 	}
     73 
     74 	// Get the IP address.
     75 	iface, err := net.InterfaceByName(b.iface)
     76 	if err != nil {
     77 		return "", fmt.Errorf(
     78 			"failed to get interface %s: %v", b.iface, err,
     79 		)
     80 	}
     81 
     82 	var ip4, ip6 string
     83 	addrs, err := iface.Addrs()
     84 	if err != nil {
     85 		return "", fmt.Errorf("failed to get addresses: %v", err)
     86 	}
     87 
     88 	for _, addr := range addrs {
     89 		a := addr.String()
     90 		if isIPv4(a) {
     91 			ip4 = a
     92 			continue
     93 		} else {
     94 			ip6 = a
     95 		}
     96 	}
     97 
     98 	switch {
     99 	case ip4 == "" && ip6 == "":
    100 		return fmt.Sprintf(" %s%%", signal), nil
    101 	case ip4 != "" && ip6 == "":
    102 		return fmt.Sprintf(" %s %s%%", ip4, signal), nil
    103 	case ip4 == "" && ip6 != "":
    104 		return fmt.Sprintf(" %s %s%%", ip6, signal), nil
    105 	default:
    106 		return fmt.Sprintf(" %s %s %s%%", ip4, ip6, signal), nil
    107 	}
    108 }
    109 
    110 func isIPv4(s string) bool {
    111 	for i := 0; i < len(s); i++ {
    112 		switch s[i] {
    113 		case '.':
    114 			return true
    115 		case ':':
    116 			return false
    117 		}
    118 	}
    119 
    120 	return false
    121 }