package birdfmt import ( "bytes" "context" "fmt" "os/exec" "strings" ) // ShowProtocols runs `birdc [-s socket] show protocols all` and returns stdout (BIRD 2). func ShowProtocols(ctx context.Context, socket, birdcBin string) (string, error) { if birdcBin == "" { birdcBin = "birdc" } args := []string{"show", "protocols", "all"} if s := strings.TrimSpace(socket); s != "" { args = append([]string{"-s", s}, args...) } cmd := exec.CommandContext(ctx, birdcBin, args...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { msg := strings.TrimSpace(stderr.String()) if msg != "" { return "", fmt.Errorf("birdc show protocols: %w: %s", err, msg) } return "", fmt.Errorf("birdc show protocols: %w", err) } return stdout.String(), nil } // CountEstablishedBGPSessions counts BGP protocol rows whose line contains "Established" // (heuristic for `birdc show protocols` / `show protocols all` output). func CountEstablishedBGPSessions(showProtocolsOutput string) int { lines := strings.Split(showProtocolsOutput, "\n") n := 0 for _, line := range lines { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "name") || strings.HasPrefix(strings.ToLower(line), "table") { continue } if !isBGPProtocolSummaryRow(line) { continue } if strings.Contains(strings.ToLower(line), "established") { n++ } } return n } // ParseBGPProtocolStates parses `birdc show protocols all` summary rows into protocol_name -> state. func ParseBGPProtocolStates(output string) map[string]string { out := make(map[string]string) for _, raw := range strings.Split(output, "\n") { line := strings.TrimSpace(raw) if line == "" { continue } low := strings.ToLower(line) if strings.HasPrefix(low, "bird ") || strings.HasPrefix(low, "name ") || strings.HasPrefix(low, "table ") { continue } fields := strings.Fields(line) if len(fields) < 4 { continue } if !strings.EqualFold(fields[1], "BGP") { continue } state := extractBGPSessionStateLine(line) if state == "" { state = fields[3] } out[fields[0]] = state } return out } func extractBGPSessionStateLine(line string) string { known := []string{ "Established", "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", } for _, st := range known { if strings.Contains(line, st) { return st } } return "" }