// tera-serial-lease-check verifies that CH02-CH48 can acquire and release the // production tera-seriald stream path. It sends no test payload and never opens // a raw TTY directly. package main import ( "context" "encoding/json" "flag" "fmt" "io" "os" "time" "tera-clone/services/tera-api/internal/serial" ) const ( firstCheckedChannel = 2 lastCheckedChannel = 48 ) type leaseController interface { Open(context.Context, string) (io.ReadWriteCloser, error) Status(context.Context) (serial.StreamSnapshot, error) } type channelResult struct { Channel string `json:"channel"` Status string `json:"status"` OpenCloseMS int64 `json:"open_close_ms"` ErrorMessage string `json:"error,omitempty"` } type report struct { Kind string `json:"kind"` Status string `json:"status"` GeneratedAt time.Time `json:"generated_at"` FirstChannel string `json:"first_channel"` LastChannel string `json:"last_channel"` ChannelsRequested int `json:"channels_requested"` ChannelsPassed int `json:"channels_passed"` ChannelsFailed int `json:"channels_failed"` ActiveLeasesAfter int `json:"active_leases_after"` IdleStatusError string `json:"idle_status_error,omitempty"` TestPayloadSent bool `json:"test_payload_sent"` DynamicDeviceScan bool `json:"dynamic_device_scan"` FixedChannelList bool `json:"fixed_channel_list"` Results []channelResult `json:"results"` } func main() { controller := serial.NewStreamSocketClient(serial.DefaultStreamSocketPath, "tera-serial-lease-check") os.Exit(run(os.Args[1:], os.Stdout, os.Stderr, controller)) } func run(args []string, stdout, stderr io.Writer, controller leaseController) int { flags := flag.NewFlagSet("tera-serial-lease-check", flag.ContinueOnError) flags.SetOutput(stderr) perChannelTimeout := flags.Duration("per-channel-timeout", 3*time.Second, "maximum time for one open/close") confirmed := flags.Bool("operator-confirmed-ch02-ch48-open-close", false, "confirm a no-test-payload CH02-CH48 open/close check") if err := flags.Parse(args); err != nil { return 2 } if flags.NArg() != 0 || !*confirmed || *perChannelTimeout <= 0 || *perChannelTimeout > 10*time.Second { fmt.Fprintln(stderr, "require explicit CH02-CH48 open/close confirmation and a timeout up to 10s") return 2 } if controller == nil { fmt.Fprintln(stderr, "serial lease controller is required") return 2 } result := report{ Kind: "tera-serial-lease-check", Status: "pass", GeneratedAt: time.Now().UTC(), FirstChannel: "CH02", LastChannel: "CH48", ChannelsRequested: lastCheckedChannel - firstCheckedChannel + 1, TestPayloadSent: false, DynamicDeviceScan: false, FixedChannelList: true, Results: make([]channelResult, 0, lastCheckedChannel-firstCheckedChannel+1), } for number := firstCheckedChannel; number <= lastCheckedChannel; number++ { channel := fmt.Sprintf("CH%02d", number) started := time.Now() ctx, cancel := context.WithTimeout(context.Background(), *perChannelTimeout) stream, err := controller.Open(ctx, channel) if err == nil { err = stream.Close() } cancel() entry := channelResult{Channel: channel, Status: "pass", OpenCloseMS: time.Since(started).Milliseconds()} if err != nil { entry.Status = "fail" entry.ErrorMessage = err.Error() result.ChannelsFailed++ } else { result.ChannelsPassed++ } result.Results = append(result.Results, entry) } statusContext, cancelStatus := context.WithTimeout(context.Background(), 3*time.Second) status, statusErr := waitForIdle(statusContext, controller) cancelStatus() result.ActiveLeasesAfter = status.ActiveLeases if statusErr != nil { result.Status = "fail" result.IdleStatusError = statusErr.Error() fmt.Fprintln(stderr, statusErr) } if result.ChannelsFailed != 0 || result.ChannelsPassed != result.ChannelsRequested { result.Status = "fail" } if err := json.NewEncoder(stdout).Encode(result); err != nil { fmt.Fprintln(stderr, err) return 1 } if result.Status != "pass" { return 1 } return 0 } func waitForIdle(ctx context.Context, controller leaseController) (serial.StreamSnapshot, error) { ticker := time.NewTicker(25 * time.Millisecond) defer ticker.Stop() var latest serial.StreamSnapshot for { status, err := controller.Status(ctx) if err != nil { return status, err } latest = status if status.ActiveLeases == 0 { return status, nil } select { case <-ctx.Done(): return latest, fmt.Errorf("serial leases did not return to idle: %w", ctx.Err()) case <-ticker.C: } } }